堆(上)
堆的核心概述
一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。
Java堆区在JVM启动的时候即被创建,其空间大小也就确定了。是JVM管理的最大一块内存空间。(堆内存的大小是可以调节的)
所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(在新生代的伊甸园区里面每个线程划分的有私有缓冲区)
方法结束以后堆中的对象不会马上被移除,而是在垃圾回收的时候才会被移除。
堆空间细分
元空间/永久区 可以看作是方法区中的一部分,只是划分在堆中了
设置堆内存大小与OOM
Java堆区用于存储Java对象实例
可以通过选项-Xmx和-Xms来设置堆的大小
-Xms用于表示堆区的起始内存,等价于-xx: InitialHeapsize
-Xmx则用于表示堆区的最大内存,等价于-xX: MaxHeapsize
堆区中的内存大小超过“-Xmx”所指定的最大内存时,将会抛出
OutofmemoryError异常通常会将-Xms和-Xmx两个参数配置相同的值,目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小。
默认情况下:
初始内存大小:物理电脑内存大小1/64
最大内存大小:物理电脑内存大小1/4、
年轻代与老年代
JAVA堆区进一步划分可以划分为年轻代与老年代。年轻代又可以划分为Eden空间,survival0空间、survival1空间(survival空间有时也叫做from区、to区)。
堆中各个区所占的比例
配置新生代与老年代在堆结构的占比。
默认-XX: NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3
可以修改-XX: NewRatio=4,表示新生代占1,老年代占4,新生代占整个堆的1/5
在 Hot spot中,Eden空间和另外两个 Survivor空间缺省所占的比例是8:1:1
可以通过选项“-XX: Survivorratio”调整这个空间比例。比如-XX: SurvivorRatio=8
几乎所有的Java对象都是在Eden区被new出来的
绝大部分的Java对象的销毁都在新生代进行了。
新生代中80%的对象都是“朝生夕死”的
图解对象分配过程
观察堆内存空间变化的工具
打开JDK的bin目录找到
常用调优工具
基本步骤
1.new的对象先放伊甸园区。此区有大小限制
2.当伊甸园的空间填满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收( Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区
3.然后将伊甸园中的剩余对象移动到幸存者0区
4.如果再次触发垃圾回收,那么Eden区的剩余对象将放到幸存者1区,幸存者0区中剩余的对象也会转移到幸存者1区。
5.再下一次触发GC,则就是往幸存者0区放。以此类推。
6.当幸存者区中的对象的AGE达到15(默认)就会被放到老年区。
可以设置参数:-XX: MaxTenuringThreshold=< N >来改变默认Age的“顶峰”。
7.当养老区内存不足时,再次触发GC: Major GC,进行养老区的内存清理
8.若养老区执行了 Major GC之后发现依然无法进行对象的保存,就会产生OOM异常
有一些例外情况
①比如一个本该在Eden区new出来的对象,由于太大了,超出了Eden区的存放水平,则会被直接放入老年区。
②幸存者区不会触发GC,只有在Eden区触发GC时候,幸存者区也会被清扫垃圾,是被动的
③ 当从Eden区放到幸存者区的时候,如果幸存者区放不下,则也会直接将对象放入老年区
示例讲解过程
首先在Eden区new对象,当Eden区满了以后进行Minor GC,剩下的对象被放入S0区,并且设置它们的Age=1;
当Eden区又满了,又触发了Minor GC,则把Eden区剩余的对象放入到S1区设置Age=1,S0区中的幸存对象也放入S1区,并且Age+1;
当Eden区又又又满了,又触发了Minor GC,则把Eden区剩余的对象放入到S0区设置Age=1,S1区中的幸存对象也放入S0区,并且Age+1;如果这时候S1区中有Age>=15的对象,则会被放入老年区。
总结
- 针对幸存者s0,s1区的总结:复制之后有交换,谁空谁是to区
- 关于垃圾回收频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集。