《深入理解Java虚拟机》读后总结(二)JVM内存分配
2016-05-25 00:00
453 查看
[b]《深入理解Java虚拟机》读后总结[/b]
(一)Sun HotSpot JVM内存模型
(二)Sun HotSpot JVM内存分配
(三)Sun HotSpot JVM内存监控
(四)Sun HotSpot JVM垃圾回收
基于Sun HotSpot JVM
请先了解JVM内存模型在来看此篇文章
使用对JVM不同内存区域灌入数据,导致相关区域内存溢出,来验证JVM内存分配
先看一个经典问题:
String s1 = "小金子(aub)"; String s2 = "小金子(aub)"; String s3 = "小金子" + "(aub)"; String s4 = new String("小金子(aub)"); String s5 = "小金子" + new String("(aub)"); String s6 = s4.intern(); System.out.println("s1 == s2: " + (s1 == s2));//true; System.out.println("s1 == s3: " + (s1 == s3));//true; System.out.println("s2 == s3: " + (s2 == s3));//true; System.out.println("s1 == s4: " + (s1 == s4));//false; System.out.println("s1 == s5: " + (s1 == s5));//false; System.out.println("s4 == s5: " + (s4 == s5));//false; System.out.println("s1 == s6: " + (s1 == s6));//true;
原因就在与String对象特殊的内存分配方式:(Strings pool是JVM内存中运行时常量池的一部分)
1.String s1 = new String("小金子(aub)");
2.String s2 = "小金子(aub)";
3.String s3 = "小金子" + "(aub)";
虽然两个语句都是返回一个String对象的引用,但是jvm对两者的处理方式是不一样的。
对于第一种,jvm会马上在heap中创建一个String对象,然后将该对象的引用返回给用户。
对于第二种,jvm首先会在内部维护的strings pool中通过String的 equels 方法查找是对象池中是否存放有该String对象,如果有,则返回已有的String对象给用户,而不会在heap中重新创建一个新的String对象;如果对象池中没有该String对象,jvm则在heap中创建新的String对象,将其引用返回给用户,同时将该引用添加至strings pool中。
注意:使用第一种方法创建对象时,jvm是不会主动把该对象放到strings pool里面的,除非程序调用 String的intern方法
对于第三种,jvm会进行“+”运算符号的优化,两遍都是字符串常量会做类似于第二种的处理,如果“+”任意一边是一个变量,就会做类似第一种的处理。
JVM栈和Native Method栈内存分配:
JAVA中八个基本类型数据,在运行时都是分配在栈中的。在栈上分配的内存,随着数据的进栈出栈,方法运行完毕,或则线程结束时,自动被回收掉了。
测试代码如下:
public class JvmStackOOM { private int stackLength = 1; public void execute() { try { stackLeak(); } catch (Throwable e) { System.out.println("stackLength : " + stackLength); e.printStackTrace(); } } private void stackLeak() { stackLength++; stackLeak(); } }
用一个递归不断地对实例变量stackLength进行自增操作,当JVM在扩展栈时无法申请到足够的空间,将产生StackOverflowError
可以使用Jvm 参数-Xss配置栈大小,例如:-Xss2M,栈内存越大,可的栈深度越大,在内存不变的情况下,jvm可创建的线程就越少,需要合理设置。
方法区内存分配:
类信息和运行时常量将会分配到此区域。
测试代码如下:
public class JvmRuntimeConstantPoolOOM { private int runtimeConstantCount = 1; public void execute() { try { runtimeConstantLeak(); } catch (Throwable e) { System.out.println("runtimeConstantCount : " + runtimeConstantCount); e.printStackTrace(); } } private void runtimeConstantLeak() { List<String> list = new ArrayList<String>(); while (true) { list.add(String.valueOf(runtimeConstantCount++).intern()); } } }
使用String的intern()方法向方法区中灌入数据,当方法区内存不足时,抛出OutOfMemoryError: PermGen space,
也可以加载过多的类的方式,测试是否有OutOfMemoryError: PermGen space异常,如果有说明类信息也是存放在方法区中的可以
使用Jvm 参数-XX:PermSize和-XX:MaxPermSize配置栈大小,例如:-XX:PermSize=10M -XX:MaxPermSize=10M
堆内存分配:
所有对象实例及数组都会在堆上分配。
堆分为新生代和老年代。新生代分为3个区域:一个eden区,和两个survivor区(互为From、To,相对的),
新建对象时首先想eden区申请分配空间,如果空间够,就直接进行分配,否则进行一次Minor GC(新生代垃圾回收)。
Minor GC后再次尝试将对象放到eden区,如果空间仍然不够,直接在老年代创建对象。
测试代码如下:
public class JvmHeapOOM { private int bojectCount = 1; public void execute() { try { heapLeak(); } catch (Throwable e) { System.out.println("bojectCount : " + bojectCount); e.printStackTrace(); } } private void heapLeak() { List<OOMObject> list = new ArrayList<OOMObject>(); while (true) { list.add(new OOMObject()); bojectCount++; } } private class OOMObject { } }
创建多个OOMObject对象放到List中,当堆内存不足时,产生OutOfMemoryError:Java Heap space
使用Jvm 参数-Xm -Xmx -Xmn -XX:SurvivorRatio配置堆,例如:-Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
本地直接内存分配:
堆外内存,NIO相关操作将在此分配内存
使用Jvm 参数-XX:MaxDirectMemorySize配置,例如:-XX:MaxDirectMemorySize=10M
所有用到的JVM启动参数:
-Xss2M 设置JVM栈内存大小
-Xms20M 设置堆内存初始值
-Xmx20M 设置堆内存最大值
-Xmn10M 设置堆内存中新生代大小
-XX:SurvivorRatio=8 设置堆内存中新生代Eden 和 Survivor 比例
-XX:PermSize=10M 设置方法区内存初始值
-XX:MaxPermSize=10M 设置方法区内存最大值
-XX:MaxDirectMemorySize=10M 设置堆内存中新生代大小
本文出自 “AUB” 博客,请务必保留此出处http://aubdiy.blog.51cto.com/2978849/1207518
相关文章推荐
- Java 6 JVM参数选项大全(中文版)
- 深入解析JVM对dll文件和对类的装载过程
- JVM Tomcat性能实战(推荐)
- Java虚拟机JVM性能优化(二):编译器
- Java程序员必须知道的5个JVM命令行标志
- Java虚拟机JVM性能优化(三):垃圾收集详解
- 简单谈谈JVM、JRE和JDK的区别与联系
- 解析Java虚拟机中类的初始化及加载器的父委托机制
- JAVA中JVM的重排序详细介绍
- 浅谈Java的虚拟机结构以及虚拟机内存的优化
- JVM角度调试优化MyEclipse
- Java虚拟机JVM性能优化(一):JVM知识总结
- Android Studio 报错failed to create jvm error code -4的解决方法
- 解析Linux系统中JVM内存2GB上限的详解
- 了解Java虚拟机JVM的基本结构及JVM的内存溢出方式
- Java堆空间占满的gc日志实例
- JVM调优之Tomcat启动参数配置及详解(一)
- java动态代理模式
- Groovy Meta Object Protocol
- java类文件结构