JVM——垃圾回收

垃圾回收:

1.如何判断对象可以回收?

  • 1.引用计数法。
    弊端:无法解决循环引用的问题。比如两个对象相互引用。这个算法就无法解决
  • 2.可达性分析算法:
    根对象(GC Root):肯定不能被进行垃圾回收的,扫描,是否被根对象直接或间接引用,如果是,不能被回收;否则,将来解可以被当做垃圾。

MAT工具看看:

System Class:
Native Stack:
Thread:
Busy Monitor:正在加锁的对象

五种引用:(面试题)

  • 强:实线(沿着根对象能找到他,平时一般都是强引用)都不要了,才回收
  • 软:内存不足时,又没有强引用不要了,进入引用队列,里面有方法
  • 弱:发生垃圾回收,就回收了,去队列
  • 虚(必须配合引用队列使用):
  • 终结器(必须):finalize():是个优先级很低的线程

为什么不用finalize释放资源???
工作效率低,第一次得先入队,进去后,线程优先级很低,很难被执行到,很容易造成它调用的对象所占用的内存迟迟得不到释放。

软引用应用:
SoftReference

 -Xmx20m -XX:+PrintGCDetails -verbose:gc

    public static void soft() {
        // list --> SoftReference --> byte[]

        List<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());

        }
        System.out.println("循环结束:" + list.size());
        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());
        }

上面的执行结果,有四个null,那么如何清理无用的软引用???
软引用关联引用队列。当软引用关联的byte[]数组回收时,软引用自己会加入到queue中去。

public static void main(String[] args) {
        List<SoftReference<byte[]>> list = new ArrayList<>();

        // 引用队列
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for (int i = 0; i < 5; i++) {
            // 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }

        // 从队列中获取无用的 软引用对象,并移除
        Reference<? extends byte[]> poll = queue.poll();
        while( poll != null) {
            list.remove(poll);
            poll = queue.poll();
        }

        System.out.println("===========================");
        for (SoftReference<byte[]> reference : list) {
            System.out.println(reference.get());
        }

2.垃圾回收算法:

  • 标记清除:
    沿着GCRoot的引用链,如果没有GCRoot的直接间接引用,将这些标记;然后清除,释放资源(释放是指:把这些对象占用的内存地址,放在一个空闲地址列表里,并不是作清零处理)

优点:速度快(只需要把垃圾内存起始结束地址清除)
缺点:容易产生内存碎片(如果是一个大的对象,空间不连续,依然放不下,召唤出呢个内存溢出)

  • 标记整理:
    在清除过程中,把可用的对象向前移动,是内存更为紧凑。
    优点:解决了内存碎片
    缺点:效率低了,因为牵扯到了对象的移动,它的地址牵扯,慢。

  • 复制:
    把内存划分为两块 FROM TO
    先标记 把FROM中存活的过程中移到TO(过程中会解除内存碎片)
    然后交换FROM TO
    TO总是空闲的一片区域

缺点;会占用双倍的内存空间
优点:没有内存碎片

3.分代回收

新生代(伊甸园+幸存区(FROM)+幸存区(TO))
老年代

针对生命周期的不同特点,进行垃圾回收。老年代处理长时间存活的对象。新生代回收更为频繁。

Minor GC:(新生代)
伊甸园放不下了,触发新生代复制回收
如果长期在from区存活,就把他放到老年代去。
但是老年代晋升的对象多了。新生代满了,老年代也满了,触发老年代的回收机制(Full GC)
在这里插入图片描述

为什么要停止别的 因为回收时,会牵扯到对象的复制,同时多个,就乱了。暂停时间比较短,因为大部分被清除了,复制的很少。(STW暂停时间)更长,因为存活时间更多,标记整理较慢。

4.GC相关

1.参数:
在这里插入图片描述
2.GC代码过程分析
10=8+1+1
内存溢出,线程没结束?
一个进程,对整个进程来说,还可以顶。

5.垃圾回收器

1.串行:

-XX: +UserSerialGC=Serial+SerialOld(复制(新生代)+标记整理(老年代))

单线程,堆内存小
2.吞吐量优先:单位时间内时间变短
3.响应时间优先:尽可能让SWT时间变短

2.3:多线程,内存大(适合多核cpu,适合服务器)

并行:

-XX: +UserParallelGC -XXl+UserParallel0ldGC

-XX:GCTimeRatio=ratio
-XX:MaxGCPauseMiilis=ms
-XX:ParallelGCThreads=n

并发:
concurrent
在这里插入图片描述

G1

Garbage First(Jdk9):同时注重吞吐量和低延迟;适合大内存(划分为多个等region);整体是标记+整理算法,两个区域间是复制
j8还不是默认的,需要个开关。
9就默认了。

优先收集垃圾最多的区。

老年代:卡表技术
如果引用了老年代,标记为脏卡。

写屏障(配合队列):重标记

字符串去重:
关注的是字符串数组:
之前那个是字符串对象:
在这里插入图片描述
在这里插入图片描述

     String s1=new String("hello");
        String s2=new String("hello");
发现开启了去重之后,还是false!!!
        System.out.println(s1==s2);  //false
           System.out.println(s1.equals(s2));  //true

地址不同!!!!!
equals同

GC调优:
1.确定目标:
在这里插入图片描述
2.新生代调优:
如何调优?
新生代是不是越大越好??
新生代太大 老年代就少了
full gc时间更长
建议:
新生代:25-50%
主要耗费时间在复制上。新生代最后需要复制的很少。
合理的晋升阈值:
总之协调好比例!!!!

### JVM 垃圾回收机制原理 在 Java 虚拟机 (JVM) 中,垃圾回收的主要目标是在不影响应用程序性能的情况下尽可能高效地清理未使用的对象并释放内存。为了实现这一目标,在进行垃圾回收前,首要任务是确定哪些对象可以被认为是“垃圾”,即那些不再被程序引用的对象[^1]。 #### 判断对象存活的方法 两种主要的技术用于判定对象是否仍然活跃: - **引用计数法**:此方法为每个对象维护一个计数器,记录有多少其他对象持有对该对象的引用。一旦某个对象的引用次数降为零,则认为该对象已死亡,可作为垃圾处理。然而,这种方法难以解决循环引用的问题,因此并未广泛应用于现代 JVM 实现中[^5]。 - **可达性分析算法**:这是当前大多数 JVM 使用的方式。从一组称为“GC Roots”的根节点集合出发,遍历整个对象图谱中的所有路径。如果无法通过任何一条路径到达某对象,则表明这个对象已经不可访问,因而标记为待删除状态。典型的 GC Root 包括正在执行的方法局部变量表内的本地变量、活动线程栈帧里的参数以及 JNI 引用等特殊类型的引用[^3]。 ### 垃圾回收过程概述 一旦确认了哪些对象应该被淘汰之后,接下来的工作便是实际执行回收操作。这通常涉及到两个阶段——标记和清除: - **标记阶段**:扫描堆区,查找所有可以从 GC Roots 访问到的对象,并将其标记为活体; - **清除阶段**:移除未被标记的对象,并整理剩余的空间以便后续新对象分配时能够更有效地利用碎片化区域。 值得注意的是,上述描述简化了很多细节,真实的垃圾收集流程可能会更加复杂,具体取决于所选用的具体垃圾收集器及其配置选项。 ### 不同类型的垃圾回收JVM 提供了几种不同的垃圾回收器,每一种都具有独特的特性和适用场景: - **Serial GC**:这是一种单线程工作的简单垃圾回收器,适合于小型应用或客户端环境下的短期运行任务。由于其简单的结构设计,使得它易于理解和调试,但在多核处理器上效率较低[^2]。 除了 Serial GC 外,还有 Parallel GC、CMS(Concurrent Mark-Sweep)、G1(Garbage First),ZGC 和 Shenandoah 等更为先进的并发/低延迟型垃圾回收器可供选择,它们各自优化了不同方面的需求,比如吞吐量最大化或是最小停顿时间等等。 ```java // 示例代码展示如何设置特定的垃圾回收器 public class GCTest { public static void main(String[] args) { System.setProperty("java.gc", "G1"); // 设置使用G1垃圾回收器 // ... 应用逻辑 ... } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值