JVM垃圾回收?判断垃圾对象?回收算法?垃圾回收器?常用的命令工具?Arthas命令?

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

目前是两种方式:

方式一:引用计数器

为每个对象创建一个引用计数,当有对象引用时,计数器+1,当引用释放时,计数器-1,所以,当计数器为0时,就认为可以被回收

但这种算法,存在一个问题,存在循环引用的问题。存在内存溢出的风险 OOM

public static void main(String[] args){
        One one = new One();
        Tow tow = new Tow();
        one.tow = tow;
        tow.one = one;
        one = null;
        tow = null;
    }
}
class One{
    public Tow tow;
}
class Tow{
    public One one;
}

方式二:可达性分析

从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

GC Roots的对象有:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象,即方法中创建的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(即一般说的Native方法)引用的对象

可达性算法中的不可达对象并不是立即死亡的,对象拥有一次自我拯救的机会
对象被系统宣告死亡至少要经历两次标记过程:第一次是经过可达性分析发现没有与GC Roots相连接的引用链,第二次是在由虚拟机自动建立的Finalizer队列中判断是否需要执行finalize()方法

当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。

每个对象只能触发一次finalize()方法

由于finalize()方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,不推荐大家使用,建议遗忘它。

2. java的垃圾回收机制?

我们通常指的垃圾回收,指的就是回收堆的内存
我们创建的对象都保存在堆中,java虚拟机通过垃圾自动回收机制,简称GC

在java中,我们可以调用System.gc()来表示要进行垃圾回收,不过不建议使用,因为使用之后,虽然不会立即触发Full GC(堆内存全扫描),而是由虚拟机来决定执行时机,但是一旦执行,还是会停止所有的活动stop the world),对应用影响很大。

一般建议,在一个对象不需要再被使用时,将其设置为null,这样GC虽然不会立即回收该对象的内存,但是会在下一次GC循环中被回收。

finalize()方法,它是在释放对象内存前,由GC调用,该方法有且仅被调用一次,一般不建议重写和调用该方法。手动调用finalize方法后,如果没有回收,则以后的GC不会再回收该对象了。

3. JVM的垃圾回收算法?

  1. 分代垃圾回收算法
    主流的虚拟机大都采用分代收集算法,它根据对象存活周期的不同,而将内存划分为多块区域。一般就是我们耳熟能详的新生代和老年代,然后再各自采用不同的回收算法

新生代(Eden),对象的存活率低,所以采用复制算法。
老年代(Old),对象的存活率高,所以采用标记清除或标记整理算法对象会优先分配到新生代,如果长时间存活或者对象过大会直接分配到老年代(新生代空间不够)。
在这里插入图片描述

  1. 复制算法
    大多数对象在新生代中被创建,其中很多对象的生命周期很短。每次新生代的垃圾回收(又称Minor GC)后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收
    对象复制算法是为了解决效率问题,它将内存一分为二,每次只使用其中一块,当这一块内容用完了,就将存活的对象复制到另一个块上,然后将另一块内存一次清理掉,这样回收的效率也就提升了,也不存在内存碎片的问题。
    算法优点是回收效率高,不存在内存碎片,但是浪费内存一半的内存空间,另外在对象存活率高的情况下,采用复制算法,效率将会变低
    在这里插入图片描述

  2. 标记清除算法
    标记清除算法是现在垃圾算法的思想基础,它将垃圾回收分为两个阶段:标记阶段和清除阶段
    标记清除算法的缺点:1,效率不高。2,该算法会产生不连续的内存碎片,当我们需要分配较大对象时,会因为无法找到足够的连续内存空间,而不得不再次提前触发垃圾回收,如果内存还是不够,则报内存不足异常。
    在这里插入图片描述

  3. 标记压缩算法(标记整理)
    标记压缩算法是老年代的一种回收算法, 首先,标记阶段跟标记清除算法一致;
    区别在于清理阶段,为了避免内存碎片产生,所有的存活对象会被压缩到内存的一端
    弊端:这个算法解决之前标记清除算法的碎片问题 但是标记和压缩的效率依然不高
    在这里插入图片描述

3. 垃圾回收器有哪些?

做垃圾回收的时候,都有一个统一的特点,叫Stop the world.
往回收效率越来越高的方向来走的,垃圾回收的时间(stop the world)在变短

  1. 单线程回收器 Serial 串行
    采用单个线程的方式来进行回收,效率一般。服务器是多核CPU,资源无法得到更好利用
    在这里插入图片描述

  2. 多线程回收器 Parallel 并行 可以充分利用CPU资源
    在这里插入图片描述

  3. CMS回收器

    • 初始化标记 这个时候会stop the world,但是由于我们只是标记GCRoot,所以花费的时间很短
    • 并发标记 一边可以继续往下跟踪,做可达性分析,相比比较耗时 一边可以让程序继续运行,可能重新创建对象,也可能创造垃圾
    • 重新标记 处理在并发标记过程中,再次产生新的垃圾,stop the world
    • 并发回收 一边针对我们刚才的垃圾对象进行回收一边程序继续运行 在这里插入图片描述
  4. G1垃圾回收器 Garbage First 垃圾优先
    将内存划分多个块 ,每个块再独立进行回收
    内存模型是实际不分代,但是逻辑上是分代的。在内存模型中,对于堆内存就不再分老年代和新生代,而是划分成一个一个的小内存块,叫做Region,每个 Region可以隶属于不同的年代。
    G1的GC分为5个阶段:
    1. 初始标记 标记出GCRoot直接引用的对象。STW
    2. 标记Region,通过RSet标记出上一个阶段标记的Region引用到的Old区Region。
    3. 并发标记阶段:跟CMS的步骤是差不多的。只是遍历的范围不再是整个Old区,而只需要遍历第二步标记出来的Region。
    4. 重新标记: 跟CMS中的重新标记过程是差不多的。
    5. 垃圾清理:与CMS不同的是,G1可以采用拷贝算法,直接将整个Region中的对象拷贝到另一个Region。而这个阶段,G1只选择垃圾较多的Region来清理,并不是完全清理。在这里插入图片描述

    CMS的核心算法就是三色标记。
    三色标记:是一种逻辑上的抽象。将每个内存对象分成三种颜色: 
    黑色:表示自己和成员变量都已经标记完毕。 
    灰色:自己标记完了,但是成员变量还没有完全标记完。
    白色:自己未标记完。
    
    CMS通过增量标记increment update 的方式来解决漏标的问题。
    

4. 常见JVM参数?

  • Xms 2g:堆初始值 2g;

  • Xmx 4g:堆最大内存 4g;

  • Xmn 新生代堆最大可用值

  • XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;

  • XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;

  • 设置最大堆内存
    参数: -Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags

  • 设置新生代与老年代优化参数
    -Xmn 新生代大小,一般设为整个堆的1/3到1/4左右
    -XX:SurvivorRatio 设置新生代中eden区和from/to空间的比例关系n/1

  • 设置新生代比例参数
    参数: -Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

在实际工作中,应该根据系统的特点做出合理的配置,基本策略:尽可能将对象预留在新生代,减少老年代的GC次数
在JVM启动参数中,可以设置跟内存、垃圾回收相关的一些参数设置,默认情况不做任何设置JVM会工作的很好,但对一些配置很好的Server和具体的应用必须仔细调优才能获得最佳性能。通过设置我们希望达到一些目标:
1.GC的时间足够的小
2.GC的次数足够的少
3.发生Full GC的周期足够的长

5. 查看JVM运行情况常用的命令?工具?

查看JVM(Java虚拟机)运行情况常用的命令?

  1. 查找Java进程
  • 使用ps命令结合grep查找Java进程,例如:ps -ef | grep java 或 ps -ef | grep tomcat。
  • 使用jps命令显示当前系统中所有Java进程的信息,例如:jps -l。
  1. 查看JVM状态信息
  • 使用jstat命令监视JVM的各种状态信息,如内存使用情况、GC情况等。例如:jstat -gcutil PID 2s,其中PID是JVM进程的ID,2s表示每2秒更新一次数据。
  • 使用jmap命令生成JVM的堆转储快照,用于分析内存使用情况。例如:jmap -heap PID。
  • 使用jstack命令生成JVM的线程转储快照,用于分析线程问题,特别是死锁等。例如:jstack -l PID。
  1. 调整JVM配置和运行参数
  • 使用jinfo命令显示JVM的配置信息,并可以修改运行时的Java进程的运行参数。例如:jinfo -flag MaxPermSize PID。
  1. 图形化监控工具
  • 使用jconsole,这是一个Java GUI监视工具,可以以图表化的形式显示各种数据,并可通过远程连接监视远程的服务器VM。
  1. 其他命令
  • jcmd:向正在运行的Java进程发送诊断命令。
  • jhat:与jmap搭配使用,用来分析jmap生成的dump文件。它内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。
  1. 命令参数详解
    -对于jstat、jmap等命令,通常可以通过添加不同的参数选项来获取不同的信息。
    -例如,jstat的-gc参数用于显示垃圾回收器的状态信息,-class参数用于显示类加载器的状态信息等。

查看工具?
Jdk1.8自带工具:

  • Jconsole
  • JVisualVM 选择Tomcat服务的端口进行监控
  • jProfiler整合idea和eclipse工具。
  • Spring Boot Admin 监控和管理Spring Boot应用程序的开源工具。
  • 阿里的arthas,实时监控和诊断:Arthas允许开发者实时监控Java应用的运行状态,包括类加载信息、JVM性能指标、线程堆栈等,帮助开发者快速定位问题

6. Arthas常用命令

  1. JVM相关命令
    dashboard
    功能:查看JVM的总体信息,包括线程、内存和运行环境等。
    thread
    功能:查看某个线程或所有线程的信息,包括线程ID、线程名、线程优先级、CPU使用率等。
    示例:
    thread:查看当前JVM的所有线程信息。
    thread -n 5:查看前5个最繁忙的线程信息。
    thread [id]:查看指定ID的线程堆栈信息。
    jvm
    功能:显示当前JVM的详细信息,如内存管理器、垃圾收集器、类加载等。
    示例:直接输入jvm命令即可查看。
    memory
    功能:查看JVM的内存分布和使用情况。
    示例:直接输入memory命令即可查看。

  2. 监控类命令
    monitor
    功能:监控指定类中方法的执行成功/失败情况,统计方法被调用的次数等。
    示例:monitor -c 5 com.example.MyClass myMethod 每5秒统计一次myMethod方法的调用次数。
    watch
    功能:“精确”监控某指定类中方法的参数变化/返回值变化情况。
    示例:watch com.example.MyClass myMethod “{params,returnObj,throwExp}” 监控myMethod方法的参数、返回值和异常。
    trace
    功能:打印某方法调用路径,并输出每个节点的耗时。
    示例:trace com.example.MyClass myMethod 跟踪myMethod方法的调用路径和耗时。
    stack
    功能:获取当前方法的被调用路径。
    示例:stack com.example.MyClass myMethod 查看myMethod方法的调用堆栈。
    tt
    功能:“精确”监控某指定类中方法的参数变化/返回值变化情况,并可以记录方法调用的状态。
    示例:tt -t com.example.MyClass myMethod 记录myMethod方法的调用状态。
    profiler
    功能:生成火焰图,用于分析CPU和内存使用情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值