堆内存(heap):
一个JVM实例只有一个堆内存,堆也是Java内存管理的核心区域,堆在JVM启动的时候创建,其空间大小也被创建,是JVM中最大的一块内存空间,所有线程共享Java堆,物理上不连续的逻辑上连续的内存空间,几乎所有的实例都在这里分配内存,在方法结束后,堆中的对象不会马上删除,仅仅在垃圾收集的时候被删除,堆是GC(垃圾收集器)执行垃圾回收的重点区域。
堆空间细分:
Java7及以前将堆空间逻辑上分成三部分:新生区+养老区+永久代
Java8及以后将堆内存逻辑上分为:新生区+养老区+元空间
新生代:
1.新生代使用了复制算法
2.新生代为gc的重点对象,经官方测试70%对象都生命周期都会在新生代中完结
3.新生代又分为了eden、survivor1、survivor2,对象创建先放在eden中,经过一定时间还幸存就会放在幸存者区
4.内存比例分默认为:8:1:1
5.新生代收集器:Minor GC/Young GC
eden(新生区):
当初始加载对象时会进入新生区
survivor(幸存区):
1.幸存区又分为from 和 to —谁为空谁为to ,始终都会有一个区域为空。
2.幸存区不会主动进行垃圾回收,只会eden回收时才会附带进行gc
3.当在幸存区中的阈值达到了15后(默认15可修改)会自动进入老年代(当然也会有特殊的优化:如当survivor区域中相同年龄的内存总和大于survivor的一半内存,会将大于等于平均年龄的对象提前放入老年代)
4.当新生区(eden)出现了内存不足时,会进行YGC,那么会将没有指针的对象回收,还有指针引向的对象放入survivor1或者survivor2区域中,eden清空,数据放入一个survivor中。当第二次进行gc那么会将eden数据放入另一个空的survivor中,并且将当前survivor中有效数据,放入空的survivor中,以此类推。
老年代:
1.较大的对象数据会放入老年代
2.当在幸存区中的阈值达到了15后(默认15可修改)会自动进入老年代
3.老年代的数据都是相对于持久的不会频繁的gc
4.(MajorGC / Old GC) 在进行majorgc时会至少进行一次minorGc ,而且majorgc的效率是比minorGc 慢10倍的
5.老年代收集器:MajorGC / Old GC 要区分与Full GC
我的理解:新时代的区域垃圾回收效率会比较高,老年代的垃圾回收效率通常会很低,垃圾回收会触发STW(stop the world),应该尽量避免老年代频繁GC.如果对象在EDEN出生并且经过一次MinnorGC后依然存活,并且能被Survivor容纳的话,将会被放在幸存者区,并将对象年龄设为1,每熬过一次MinnorGC,age增加一岁,当age增加到15时,会被放入老年代
GC ROOTS :
在java语言中,可以作为GC ROOTS的对象包括下面几种:
1.虚拟机栈(栈帧中的本地变量表)中引用的对象
2.方法区中类静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中JNI(一般说的是Native方法)引用的对象
我的理解:被GC ROOTS所持有的对象不会被垃圾回收器回收
jvm堆内存查看工具: jvisualvm
工具-插件 安装GC插件
代码
import java.util.ArrayList; import java.util.List; public class HeapTest { public static void main(String[] args) { Listlist = new ArrayList<>(); while (true){ list.add(new HeapTest()); } } }
用jvisualvm的GC插件可以看到如下
因为HeapTest不断的被创建,又被GC ROOTS引用,没办法回收,所以当堆内存满了以后会抛出异常
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)