了解JVM欸村的方法有很多,具体能力范围也有区别,简单总结如下:
1、使用综合性的图形化工具,如JConsole、VisualVM(从Oracle JDK9开始,VisualVM已经不再包含在JDK安装包中)等。这些工具具体使用起来相对比较直观,直接连接到Java进程,然后就可以在图形化界面里掌握内存使用情况。
以JConsole为例,其内存页面可以显示常见的堆内存和各种堆外部分使用状态
2、也可以使用命令行工具进行运行时查询,如jstat和jmap等工具都提供了一些选项,可以查看堆、方法区等使用数据。
3、也可以使用jmap等提供的命令,生成堆转储(Heap Dump)文件,然后利用jhat或Eclipse MAT等堆转储分析工具进行详细分析。
堆内部是什么结构?
对于堆内存,按照通常的GC年代方式划分,java堆内分为:
1、新生代
新生代是大部分对象创建和销毁的区域,在通常的java应用中,绝大部分对象生命周期都是很短暂的。其内部又分为Eden区域,作为对象初始分配的区域;两个Survivor,有时候也叫from、to区域,被用来放置从Minor GC中保留下来的对象。
堆结构示意图JVM会随意选取一个Survivor区域作为“to”,然后会在GC过程中进行区域间拷贝,也就是将Eden中存活下来的对象和from区域的对象,拷贝到这个“to”区域。这种设计主要是未了防止内存的碎片化,并进一步清理无用对象;
从内存模型而不是垃圾收集的角度,对Eden区域继续进行划分,Hotspot JVM还有一个概念叫做Thread Local Allocation Buffer(TLAB),据我所知所有OpenJDK衍生出来的JVM都提供了TLAB的设计。这是JVM为每个线程分配的一个私有缓存区域,否则,多线程同时分配内存时,为避免操作同一地址,可能需要使用加锁等机制,进而影响分配速度;从上图中可以看出,TLAB仍然在堆上,它是分配在Eden区域内的。其内部结构比较直观易懂,start、end就是起始地址,top(指针)则表示已经分配到哪里了。所以我们分配新对象,JVM就会移动top,当top和end相遇时,即表示该缓存已满,JVM会试图再从Eden里分配一块儿。
2、老年代
放置长生命周期的对象,通常都是从Survivor区域拷贝过来的对象,当然也有特殊情况,我们知道普通的对象会被分配再TLAB上;如果对象较大,JVM会试图直接分配再Eden其他位置上;如果对象太大,完全无法再新生代找到足够长的连续空闲空间,JVM就会直接分配到老年代。
3、永久代
这部分就是早期Hotspot JVM的方法区实现方式了,存储Java类元数据、常量池、Intern字符串缓存,再JDK8之后就不存在永久代这块儿了。
如何利用JVM参数,直接影响堆和内部区域的大小?
最大堆体积
-Xmx value
初始的最小堆体积
-Xms value
老年代和新生代的比例
-XX:NewRatio=value
默认情况下,这个数值是2,意味着老年代是新生代的2倍大;换句话说,新生代是堆大小的1/3;
当然,也可以不用比例的方式调整新生代的大小,直接指定下面的参数,设定具体的内存大小数值:
-XX:NewSize=value
Eden和Survivor的大小是按照比例设置的,如果SurvivorRatio是8,那么Survivor区域就是Eden的1/8大小,也就是新生代的1/10,因为YoungGen=Eden+2*Survivor,JVM参数格式是:
-XX:SurvivorRatio=value
网友评论