Java虚拟机总结

1、JVM如何判断哪些对象是需要回收的?

  • 判断对象是否存活的算法有引用计数算法和可达性分析算法两种,由于引用计数算法很难解决对象之间相互循环引用的问题,所以JVM采用的是可达性分析算法。

  • 可达性分析算法:

    从GC Roots出发向下搜索,所走过的路径称为引用链,当没有任何一条引用链经过该对象时,则证明该对象不可用,应该被回收。

  • 可以作为GC Roots的对象包括下面几种:

    (1)虚拟机栈(栈帧中的本地变量表)中引用的对象;
    (2)本地方法栈中JNI(Native方法)引用的对象;
    (3)方法区中类静态属性引用的对象;
    (4)方法区中常量引用的对象。

2、JVM的参数配置(调优)

  • JVM中的参数有三种类型:

    (1)标配参数:java -version、java -help、java -showversion等。
    (2)X参数:-Xint(解释执行)、-Xcomp(编译执行)、-Xmixed(混合模式)。
    (3)XX参数:重点掌握

  • XX参数又分为两种类型:

    (1)Boolean类型:-XX:+ 表示开启,-XX:- 表示关闭。例:-XX:+PrintGCDetails,开启打印GC的详细信息。-XX:+UseSerialGC,启用串行垃圾收集器。
    (2)KV设置类型:-XX:属性key=属性value。例:-XX:MetaspaceSize=128m,设置元空间的大小为128m。

  • 那么-Xms和-Xmx是属于X参数还是XX参数呢?

    是属于XX参数,-Xms20m等价于-XX:InitialHeapSize=20m,-Xmx20m等价于-XX:MaxHeapSize=20m,只不过由于使用的频率高,被简化过了。

  • 查看参数配置的方法

    (1)jinfo

    可以用 " jinfo -flag 属性key 进程编号" 命令查看具体某个属性值。
    可以用 " jinfo -flags 进程编号" 命令查看所有可查看的属性值。

    (2)使用XX参数

    java -XX:+PrintFlagsInitial,打印JVM所有参数的默认值
    java -XX:+PrintFlagsFinal,打印JVM所有参数的修改后的值(显示为" = “的为没有修改过的默认值,显示为” := "的是人为修改过的或JVM自动优化过的)
    java -XX:+PrintCommandLineFlags,打印命令行参数(主要是查看垃圾收集器)

  • JVM常用基础参数

    (1)-Xms,堆初始内存大小。默认为物理内存的1/64,等价于-XX:InitialHeapSize。
    (2)-Xmx,堆最大分配内存。默认为物理内存的1/4,等价于-XX:MaxHeapSize。
    (3)-Xss,栈空间的初始内存大小。一般默认为512k~1024k,
    等价于-XX:ThreadStackSize。
    (4)-Xmn,设置年轻代的大小。一般用默认即可。
    (5)-XX:MetaspaceSize,设置元空间大小。元空间的本质和永久代类似,都是对方法区的实现,元空间与永久代之间最大区别在于:元空间不在虚拟机中,而是使用本地内存,默认情况下,元空间的大小仅受本地内存大小的限制。
    (6)-XX:+PrintGCDetails,输出详细GC收集日志信息。
    (7)-XX:SurvivorRatio,设置新生代中eden和S0/S1空间的比例。默认为8。
    (8)-XX:NewRatio,设置老年代和新生代所占的空间比例。
    (9)-XX:MaxTenuringThreshold,设置对象从新生代进入老年代所需经过的垃圾收集次数。默认为15。

3、强引用、软引用、弱引用、虚引用

  • 强引用

使用赋值方式的引用,类似" Object o = new Object(); "这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

  • 软引用(SoftReference类)

系统内存充足的时候,垃圾收集时不会回收软引用对象,系统内存不够用的时候才会回收软引用对象。

  • 弱引用(WeakReference类)

不管内存是否够用,只要发生了垃圾回收,就会回收弱引用对象。

  • 虚引用(PhantomReference类)

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。它不能单独使用,也不能通过它来访问对象,PhantomReference的get()方法总是返回null,虚引用必须和引用队列(ReferenceQueue)联合使用。
设置虚引用的目的,是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。

  • 引用队列

对象在被垃圾收集器回收之前都要在引用队列里保存一下。引用队列一般是作为参数传入引用类的构造方法里面,当引用的对象没有被回收时,引用队列里没有没有该对象,当引用的对象被回收时,会存在引用队列中。

  • 软引用和弱引用的应用场景

假如有一个应用需要读取大量的本地图片:如果每次读取图片都从硬盘读取则会严重影响性能,如果一次性全部加载到内存中又可能造成内存溢出。
软引用可以用来解决这个问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM问题。
Map<String,SoftReference<Bitmap>> imageCache = new HashMap<>();

弱引用常用来设计高速缓存:使用WeakHashMap,当发生垃圾回收时,WeakHashMap里的键值对会被回收。

4、JVM异常类型

  • JVM异常的继承体系
    在这里插入图片描述

  • (1)SOFE(StackOverFlowError):栈空间内存溢出

  • (2)OOM(OutOfMemoryError):内存空间溢出,JVM有多个区域有可能引发这个异常:

    • java.lang.OutOfMemory:java heap space,堆内存溢出。
    • java.lang.OutOfMemory:GC overhead limit exceeded,超过98%的时间用来做GC并且回收了不到2%的堆内存。
    • java.lang.OutOfMemory:Direct buff memory,直接内存溢出。

    常发生于NIO编程,NIO编程经常使用ByteBuffer来读取或写入数据,而ByteBuffer有两种内存分配方式:第一种,ByteBuffer.allocate(capability),分配JVM堆内存,属于GC管辖范围,第二种,ByteBuffer.allocateDirect(capability),分配os本地内存,不属于GC管辖范围,在JVM堆中用DirectByteBuffer对象保存这块内存的引用。如果不断分配本地内存,堆内存很少使用,JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收,本地内存用光了之后,再次尝试分配本地内存就会发生这个异常。

    • java.lang.OutOfMemory:unable to create new native thread,不能创建更多的线程。一个应用进程创建了太多个线程,超过系统承载极限(Linux系统默认允许单个进程可以创建的线程数是1024个),常见于并发编程。
    • java.lang.OutOfMemory:Metaspace,元空间内存溢出。

5、垃圾收集器

  • 怎么查看JVM默认的垃圾收集器?
    使用 “java -XX:+PrintCommandLineFlags” 命令,打印一些系统初始的参数,Java8默认使用的垃圾收集器是-XX:+UseParallelGC,并行垃圾收集器。

  • Java8以后的新老生代垃圾收集器组合推荐
    在这里插入图片描述
    (1)Serial + Serial Old
    (2)ParNew + CMS + Serial Old
    (3)Parallel Scavenge + Serial Old
    (4)Parallel Scavenge + Parallel Old

  • GC日志相关参数说明
    在这里插入图片描述

  • Serial

Serial是作用在新生代的单线程垃圾收集器,由于使用单个线程进行垃圾回收,因此会发生较长时间的Stop The World,对于限定单个CPU的环境来说,没有线程交互的开销,可以获得较高的垃圾回收效率。它是JVM运行在Client模式下默认的新生代垃圾收集器。


对应JVM参数:-XX:+UseSerialGC
开启后会使用:Serial + Serial Old组合

  • ParNew

ParNew其实就是Serial收集器新生代的并行多线程版本,在垃圾收集的过程中也要暂停所有其他的工作线程。它是JVM运行在Server模式下默认的新生代垃圾收集器。


对应JVM参数:-XX:+UsePaeNewGC
开启后会使用:ParNew + Serial Old组合(不推荐)

  • Parallel Scavenge

Parallel Scavenge是作用于新生代的并行垃圾收集器,它注重的是获得更大的吞吐量,适用于后台运算而不需要太多交互的任务。而它与ParNew的一个主要区别是,它具有自适应调节策略,虚拟机会根据当前系统的运行状况收集性能监控信息,动态地调整最大垃圾收集停顿时间(也可以通过-XX:MaxGCPauseMills手动设置)或吞吐量大小(-XX:GCTimeRatio手动设置)。


对应JVM参数:-XX:+UseParallelGC
开启后会使用:ParallelScavenge + Parallel Old组合

  • Parallel Old

Parallel Old是Parallel Scavenge的老年代版本,老年代的并行垃圾收集器,在JDK1.6之后出现,所以之前Parallel Scavenge只能和Serial Old组合,而逐渐地Serial Old已经被淘汰,所以现在Parallel Scavenge一般是和Parallel Old组合。


对应JVM参数:-XX:+UseParallelOldGC
开启后会使用:ParallelScavenge + Parallel Old组合

  • CMS

CMS全称是Concurrent Mark Sweep,即并发标记清除,是一种以获取最短回收停顿时间为目标的垃圾收集器,并发是指垃圾收集线程和用户线程并发执行。适合应用在互联网站或B/S系统的服务器上。工作过程包括四个阶段:初始标记(标记GC Roots能直接关联的对象)、并发标记(进行GC Roots跟踪,标记全部对象)、重新标记(修正并发标记期间由于用户程序运行而导致的对象关系间的变化)、并发清除(回收GC Roots不可达对象)。
在这里插入图片描述
对应JVM参数:-XX:+UseConcMarkSweepGC
开启后会使用:ParNew + CMS + Serial Old组合,Serial Old作为CMS出现Concurrent Mode 失败的备选收集器。

  • Serial Old

Serial Old是Serial的老年代版本,单线程垃圾收集器。Java8中已被优化掉,不再使用

  • G1

G1(Garbage first),是一种注重垃圾收集价值的收集器。
不同于其他的分代回收算法、G1将堆空间划分成了互相独立的区块。每块区域既有可能属于O区、也有可能是Y区,且每类区域空间可以是不连续的(对比CMS的O区和Y区都必须是连续的)。这种将O区划分成多块的理念源于:当并发后台线程寻找可回收的对象时、有些区块包含可回收的对象要比其他区块多很多。虽然在清理这些区块时G1仍然需要暂停应用线程、但可以用相对较少的时间优先回收包含垃圾最多区块。这也是为什么G1命名为Garbage First的原因:第一时间处理垃圾最多的区块。


G1相对于CMS的区别在:
(1)G1在压缩空间方面有优势
(2)G1通过将内存空间分成区域(Region)的方式避免内存碎片问题 Eden, Survivor, Old区不再固定、在内存使用效率上来说更灵活
(3)G1可以通过设置预期停顿时间(Pause Time)来控制垃圾收集时间避免应用雪崩现象
(4)G1在回收内存后会马上同时做合并空闲内存的工作、而CMS默认是在STW(stop the world)的时候做
(5)G1会在Young GC中使用、而CMS只能在O区使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值