作者:Chilldon螴暁鼕 | 来源:互联网 | 2023-09-09 18:21
一、Java1.8堆内存变化 JDK8完全移除永久代PermanentGeneration,取而代之的是元空间Metaspace(JVM使用本地内存,存放类的元数据)。1.元空间
一、Java 1.8 堆内存变化
JDK8 完全移除永久代 Permanent Generation, 取而代之的是元空间 Metaspace(JVM使用本地内存,存放类的元数据)。
1. 元空间的内存分配模型
大多数的类元数据分配在本地内存中,其大小受到本地可用内存的限制。可通过MaxMataspaceSize 来设置,当内存占用达到此值,将会触发对死亡对象和类加载器的GC;未设置时,也可根据应用程序运行时需求动态设置。
2. 堆内存的影响
一些数据,如String等转移到Java Heap中,因此堆空间会不断增加。
二、堆内存的划分
相比栈内存,堆内存大的多,JVM通过对堆内存划分不同的功能区块实现对堆内存中对象管理。堆内存通常被分为两块区域年轻代(young generation)、长老代(old generation)。
1. New一个对象的过程:
一个对象被创建以后首先存放到年轻代中的Eden内存中,如果存活期超几个Survivor之后就会被转移到长老代内存(Old Generation)中。
2. 内存使用过程:
年轻代将内存分为一块比较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。
HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是说,每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的空间会被浪费。
三、GC算法 — 采取分代GC算法
1. MinorGC是发生在新生代中的GC,由于对象回收概率大,GC频繁且回收速度较快,采用的是复制算法。
2. FullGC是发生在老年代的GC动作,当前主要采用的是标记-清除/整理算法。
老年代里的对象较少且不那么容易死掉。因此Full GC发生的次数不会那么频繁,并且一次Full GC要比一次Minor GC的时间长。
标记:(标记活着的对象)采用对象引用遍历,从一组GC Root对象开始(GC Root包括局部变量,静态变量及线程对象),沿着整个对象图上的每条链接,递归确定可到达(reachable)的对象。如果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集。在对象遍历阶段,GC必须记住哪些对象可以到达,以便删除不可到达的对象;
清除:GC删除不可到达的对象。删除时,有些GC只是简单的扫描堆栈,删除未标记的未标记的对象,并释放它们的内存以生成新的对象。
压缩/整理:这种方法的问题在于内存会分成好多小段,而它们不足以用于新的对象,但是可以将其组合起来。因此,许多GC可以重新组织内存中的对象,并进行压缩(compact),形成可利用的空间。
三、Java 1.8 GC变化
在执行机制上JVM提供了串行GC(SerialGC)、并行回收GC(ParallelScavenge)和并行GC(ParNew):
1. 串行GC
在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的GC方式,可以通过-XX:+UseSerialGC来指定;
2. Parallel GC — (Java1.8 的默认GC),它的速度是最快;
并行回收GC:在新生代采用复制算法,在老年代采用标记–压缩算法,在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来指定,用-XX:ParallelGCThreads=4来指定线程数;
3. 并发标记清除收集器
在新生代中采用复制算法,在老年代采用标记–清除算法(不是标记–压缩)。( 之所以叫“并发”,是因为在回收过程的某些阶段,回收线程和用户线程同时执行,并不是整个回收过程都可以和用户线程并行)可以通过–XX:+UseConcMarkSweepGC 来指定;
4.G1收集器(Garbage First Collector)
G1收集器是Java世界最新的收集器,在Java 9中,它将成为默认的垃圾收集器。该收集器采用与上文中提到的收集器不同方式来对待Java对内存,可以通过-XX:+UseG1GC激活该收集器。