JVM---GC-续
分代收集算法
/**
* 【GC---分代收集算法】
* ***没有最好的算法,只有最合适的算法;
*
* 基于 不同对象的生命周期不一样,因此,不同生命周期的对象可以采取不同的收集方式,以提高回收效率;
*
* 目前几乎所有的 垃圾收集器 都采用 分代收集算法 ;
* eg:Hotspot中:
* 年轻代:
* 特点:
* 对象朝生夕死,存活率低,回收频繁;
* 使用 复制算法 Copying;
*
* 老年代:
* 特点:
* 对象生命周期长,回收不频繁;
* 使用 标记-清除Mark-Sweep 或 标记-压缩Mark-Compact 算法;
* 标记的开销 与 可达对象的数量 成正比;
* 清除的开销 与 内存区域大小 成正比;
* 压缩的开销 与 可达对象数量 成正比;
*/
增量收集算法
/**
* 【GC---增量收集算法】
* 解决什么问题?
* Mark-Sweep、Copying、Mark-Compact算法,在垃圾回收过程中,会进行STW,用户线程都被挂起;
* 如果垃圾回收时间过长,将严重影响用户体验;
* 为了解决这个问题,增量收集算法 诞生;
*
* 思想:
* 让 垃圾收集线程 与 用户线程 交替进行:
* 每次垃圾收集线程 只收集一小片区域的内存空间,接着切换到用户线程;
* 依次反复;
*
* 本质:
* 增量收集算法的基础 仍是 标记-清除、复制算法;
* 通过对线程间的妥善处理,允许垃圾收集线程以分阶段的方式完成标记-清除工作;
*
* 缺点:
* 因为线程切换和上下文切换的消耗,会使得垃圾回收的总体成本上升;
*/
分区算法
/**
* 【GC---分区算法】
* 解决什么问题?
* 在相同条件下,堆空间越大,一次GC所需要的时间越长;
* 为了更好控制GC时间,可以将一块大的区域分割成多个小块,每次合理的回收若干小块;
*
* 分区算法 与 分代算法的区别
* 分代算法:
* 按对象的生命周期,划分为新生代、老年代;
* 分区算法:
* 将整个堆空间划分为连续不同的小区间region;
* 每个小区间region独立使用,独立回收;
*
*/
分区算法

栈-栈桢-局部变量表GC测试
public class LocalVarGCTest {
static void test1(){
byte[] bytes = new byte[10*1024*1024];
System.gc();
/**
* 未回收
* [GC (System.gc()) [PSYoungGen: 14176K->10736K(76288K)] 14176K->10808K(251392K), 0.0088785 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
* [Full GC (System.gc()) [PSYoungGen: 10736K->0K(76288K)] [ParOldGen: 72K->10681K(175104K)] 10808K->10681K(251392K), [Metaspace: 3287K->3287K(1056768K)], 0.0128084 secs] [Times: user=0.01 sys=0.02, real=0.01 secs]
* Heap
* PSYoungGen total 76288K, used 655K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
* eden space 65536K, 1% used [0x000000076ab00000,0x000000076aba3ee8,0x000000076eb00000)
* from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
* to space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
* ParOldGen total 175104K, used 10681K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
* object space 175104K, 6% used [0x00000006c0000000,0x00000006c0a6e5d8,0x00000006cab00000)
* Metaspace used 3293K, capacity 4496K, committed 4864K, reserved 1056768K
* class space used 363K, capacity 388K, committed 512K, reserved 1048576K
*/
}
static void test2(){
byte[] bytes = new byte[10*1024*1024];
bytes = null;
System.gc();
/**
* 已回收
* [GC (System.gc()) [PSYoungGen: 14176K->608K(76288K)] 14176K->616K(251392K), 0.0009482 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
* [Full GC (System.gc()) [PSYoungGen: 608K->0K(76288K)] [ParOldGen: 8K->414K(175104K)] 616K->414K(251392K), [Metaspace: 3243K->3243K(1056768K)], 0.0043901 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
* Heap
* PSYoungGen total 76288K, used 655K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
* eden space 65536K, 1% used [0x000000076ab00000,0x000000076aba3ee8,0x000000076eb00000)
* from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
* to space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
* ParOldGen total 175104K, used 414K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
* object space 175104K, 0% used [0x00000006c0000000,0x00000006c0067a20,0x00000006cab00000)
* Metaspace used 3250K, capacity 4496K, committed 4864K, reserved 1056768K
* class space used 357K, capacity 388K, committed 512K, reserved 1048576K
*/
}
static void test3(){
{
byte[] bytes = new byte[10*1024*1024];
}
System.gc();
/**
* 未回收
* [GC (System.gc()) [PSYoungGen: 14176K->10720K(76288K)] 14176K->10772K(251392K), 0.0078192 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
* [Full GC (System.gc()) [PSYoungGen: 10720K->0K(76288K)] [ParOldGen: 52K->10681K(175104K)] 10772K->10681K(251392K), [Metaspace: 3287K->3287K(1056768K)], 0.0091166 secs] [Times: user=0.01 sys=0.02, real=0.01 secs]
* Heap
* PSYoungGen total 76288K, used 655K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
* eden space 65536K, 1% used [0x000000076ab00000,0x000000076aba3ee8,0x000000076eb00000)
* from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
* to space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
* ParOldGen total 175104K, used 10681K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
* object space 175104K, 6% used [0x00000006c0000000,0x00000006c0a6e5d8,0x00000006cab00000)
* Metaspace used 3294K, capacity 4496K, committed 4864K, reserved 1056768K
* class space used 363K, capacity 388K, committed 512K, reserved 1048576K
*/
}
static void test4(){
{
byte[] bytes = new byte[10*1024*1024];
}
int a = 0; // 局部变量表的slotsSize=1 ,代码块中的bytes 与 a 共用一个slot,a覆盖掉bytes,导致 bytes指向的对象为垃圾对象
System.gc();
/**
* 已回收
* [GC (System.gc()) [PSYoungGen: 14176K->608K(76288K)] 14176K->616K(251392K), 0.0010316 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
* [Full GC (System.gc()) [PSYoungGen: 608K->0K(76288K)] [ParOldGen: 8K->414K(175104K)] 616K->414K(251392K), [Metaspace: 3243K->3243K(1056768K)], 0.0046198 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
* Heap
* PSYoungGen total 76288K, used 1966K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
* eden space 65536K, 3% used [0x000000076ab00000,0x000000076aceb9e0,0x000000076eb00000)
* from space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
* to space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
* ParOldGen total 175104K, used 414K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
* object space 175104K, 0% used [0x00000006c0000000,0x00000006c00679b8,0x00000006cab00000)
* Metaspace used 3250K, capacity 4496K, committed 4864K, reserved 1056768K
* class space used 357K, capacity 388K, committed 512K, reserved 1048576K
*/
}
static void test5(){
test1();
System.gc();
/**
* test1中未回收
* test5中已回收
* [GC (System.gc()) [PSYoungGen: 14176K->10720K(76288K)] 14176K->10784K(251392K), 0.0130876 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
* [Full GC (System.gc()) [PSYoungGen: 10720K->0K(76288K)] [ParOldGen: 64K->10654K(175104K)] 10784K->10654K(251392K), [Metaspace: 3243K->3243K(1056768K)], 0.0163820 secs] [Times: user=0.01 sys=0.02, real=0.01 secs]
* [GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] 10654K->10654K(251392K), 0.0003257 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
* [Full GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] [ParOldGen: 10654K->414K(175104K)] 10654K->414K(251392K), [Metaspace: 3243K->3243K(1056768K)], 0.0054800 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
* Heap
* PSYoungGen total 76288K, used 1966K [0x000000076ab00000, 0x0000000770000000, 0x00000007c0000000)
* eden space 65536K, 3% used [0x000000076ab00000,0x000000076aceb9e0,0x000000076eb00000)
* from space 10752K, 0% used [0x000000076f580000,0x000000076f580000,0x0000000770000000)
* to space 10752K, 0% used [0x000000076eb00000,0x000000076eb00000,0x000000076f580000)
* ParOldGen total 175104K, used 414K [0x00000006c0000000, 0x00000006cab00000, 0x000000076ab00000)
* object space 175104K, 0% used [0x00000006c0000000,0x00000006c00679b8,0x00000006cab00000)
* Metaspace used 3268K, capacity 4496K, committed 4864K, reserved 1056768K
* class space used 359K, capacity 388K, committed 512K, reserved 1048576K
*/
}
public static void main(String[] args) {
LocalVarGCTest.test5();
}
}
内存泄漏

后几个对象本来不被使用了,但是还有一个引用,导致无法被回收;
垃圾回收相关概念
/**
* 【GC---垃圾回收相关概念】
* <System.gc()或Runtime.getRuntime().gc()>
* 默认情况下,通过System.gc()或Runtime.getRuntime().gc()的调用,显式触发Full GC,同时对新生代和老年代进行回收;
*
* System.gc()或Runtime.getRuntime().gc() 无法保证对 垃圾收集器 的调用;
*
* System.runFinalization() 强制 垃圾收集器 调用垃圾对象的finalize();
*
* <内存溢出OOM>
* 没有空闲内存空间 且 GC后也无法提供更多内存空间;
*
* 原因:
* 1、JVM的堆内存设置不够
* 可能存在内存泄漏问题;
* 可能是堆大小不合适;
* 2、创建大量大对象, 且 长时间被引用;
*
* 一般情况下,在抛出OOM之前,通常 垃圾收集器 被触发 进行GC;
* 特殊情况下,直接抛出OOM
* eg:分配一个超大对象,JVM判断出垃圾收集器不能解决这个问题,直接抛出OOM;
*
* <内存泄漏Memory Leak>
* what
* 严格意义:
* 只有对象不再被程序使用,但GC又不能回收;
*
* 宽泛意义:
* 一些不太好的使用 导致对象的生命周期变得很长甚至导致OOM;
*
* 场景
* 1、单例模式
* 单例对象的生命周期和应用程序一样长,如果单例对象引用其他对象,会导致外部对象不被回收,产生内存泄漏;
*
* 2、资源未关闭导致内存泄漏
* 数据库连接、网络连接、IO连接 必须手动close,否则不能被回收;
*
* <STW>
* what
* Stop The World,在GC发生过程中,导致用户线程暂停;
*
* 所有的垃圾回收器 都有STW事件;
*
* 不要显式调用System.gc(),会造成STW;
*
* <垃圾回收的并行与并发>
* 并行Parallel:
* 多个 垃圾收集线程 并行工作,用户线程被暂停;
* eg:ParNew、Parallel Scavenge、Parallel Old;
*
* 串行Serial:
* 单个 垃圾收集线程 执行;
*
* 并发Concurrent:
* 用户线程 与 垃圾收集线程 同时执行,垃圾收集线程不会暂停用户线程;
* eg:CMS、G1
*
* <安全点Safepoint与安全区域Safe Region>
* 安全点
* what
* 程序执行时,并非在所有地方都能停顿下来开始GC,只有在特定的位置才能停顿下来,这些位置叫 安全点Safepoint;
*
* 安全点的选择很重要:
* 如果太少,可能导致GC等待时间太长、如果太频繁,可能导致运行时性能;
* 大部分指令的执行时间都非常短暂,通常会根据 是否具有让程序长时间执行的特征 为标准,比如选择一些执行时间较长的指令为安全点:
* eg:方法调用、循环跳转、异常跳转...
*
* 如何在GC发生时,检查所有线程都运行到最近的安全点停顿下来?
* 抢先式中断(目前没有JVM采用):
* 首先中断所有线程,如果有线程不在安全点,恢复线程,让线程运行至安全点;
*
* 主动式中断:
* 设置一个中断标志,各个线程运行到安全点时主动轮询这个标志,如果中断标志为true,将自己线程中断挂起;
*
* 安全区域
*
* ???
*
* <Java中的引用>
* 需求:
* 希望描述一类对象,当内存空间足够时,保留在内存中;如果内存空间紧张 且 GC后还是紧张,可以抛弃;
*
* 解决:
* JDK1.2之后,Java对引用概念进行了扩展,分为 强引用Strong Reference、软引用Soft Reference、弱引用Weak Reference、虚引用Phantom Reference,引用强度依次减弱;
* 在java.lang.ref包中可以找到java.lang.ref.SoftReference、java.lang.ref.WeakReference、java.lang.ref.PhantomReference
*
* <强引用>
* what
* 传统引用的定义即引用赋值,如 Object o = new Object();
* 强引用关系,无论任何情况下,只要强引用关系存在,垃圾收集器 不会回收 被强引用的对象;
* 特点:
* 强引用可以直接访问目标对象;
* 强引用对象都是可达的;
* 对于一个强引用对象,如果没有任何引用关系(超过引用关系作用域或显式赋值为null),就被当做垃圾对象;
*
* ***强引用也是造成Java内存泄漏的主要原因之一;
*
* <软引用>
* what
* 在系统将发生OOM之前,会把这些软引用对象列入回收范围进行二次回收,如果回收后还没足够的内存空间,才会抛出OOM;
*
* JDK实现:
* java.lang.ref.SoftReference
*
* 场景:
* 缓存
*
* <弱引用>
* what
* 只要GC,无论内存空间是否足够,都会被回收;
*
* JDK实现:
* java.lang.ref.WeakReference
*
* eg:
* WeakHashMap
*
* java.util.WeakHashMap{
* Entry<K,V>[] table;
*
* private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {}
* }
*
* <虚引用>
* what
* 一个对象是否有虚引用存在,完全不会对该对象的生命周期产生影响;
* 无法通过虚引用获得一个对象实例;
*
* 目的
* 能在这个对象被 垃圾收集器GC时,收到一个系统通知;
*/
public class ReferenceTest {
public static void main(String[] args) {
// softTest();
weakTest();
}
static void weakTest(){
// 建立弱引用
User user = new User(1, "rose");
WeakReference<User> weakReference = new WeakReference<User>(user);
// 移除强引用
user = null;
// 弱引用获取对象
System.out.println(weakReference.get());
System.gc();
System.out.println("after gc...");
System.out.println(weakReference.get()); // 一旦GC,就回收弱引用对象
/**
* 结果:
* User{age=1, name='rose'}
* after gc...
* null
*/
}
static void softTest(){
// -Xms10m -Xmx10m
// 建立软引用
User user = new User(1, "rose");
SoftReference<User> softReference = new SoftReference<User>(user);
// 移除强引用关系
user = null;
// 软引用获取对象
System.out.println(softReference.get());
System.gc();
System.out.println("after gc...");
System.out.println(softReference.get()); // 由于内存空间足,不会回收软引用对象
try {
byte[] bytes = new byte[1024*1024*7];
}catch (Throwable e){
e.printStackTrace();
}finally {
System.out.println(softReference.get()); // 由于内存空间不足,回收软引用对象
}
/**
* 结果:
* User{age=1, name='rose'}
* after gc...
* User{age=1, name='rose'}
* java.lang.OutOfMemoryError: Java heap space
* at com.an.gc.ReferenceTest.softTest(ReferenceTest.java:34)
* at com.an.gc.ReferenceTest.main(ReferenceTest.java:14)
* null
*/
}
private static class User{
private int age;
private String name;
public User(int age, String name){
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
}
浙公网安备 33010602011771号