JVM和垃圾回收

最近关于JVM和垃圾回收,阿里云上进行了具体的学习巩固,以下是学习总结。

内容介绍:

一、JVM
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。

二、JVM 组成
在这里插入图片描述

  • Class Files: .java文件,通过 java 编译成 .class文件。
  • Class Loader Subsystem:将所需的类加载到内存,必要时将类实例化成实例。
  • Runtime Data Areas:JVM 运行时区域,图中中间部分是进程的内存逻辑结构,由下面几部分构成:
    • Heap:堆,所有线程共享的内存空间,存放创建的所有对象。堆是靠 GC 垃圾回收器管理的。
    • Java Threads:Java 栈,每个线程会分配一个栈,存放线程用的本地变量、方法参数和返回值等。
    • Program Counter Registers:PC 寄存器,每一个线程用于记录当前线程正在执行的字节码指令地址。因为线程需要切换,当一个线程被切换回来需要执行的时候,知道执行到哪里了。
    • Method Area:方法区,所有线程共享的内存空间,存放已加载的类信息、常量和静态变量。
    • Native Internal Threads:本地方法栈,为本地方法执行构建的内存空间,存放本地方法执行时的局部变量、操作数等。所谓本地方法,简单的说是非 java 实现的方法,例如操作系统的 C 编写的库提供的本地方法,Java 调用这些本地方法接口执行。本地方法应该避免直接编程使用,因为 Java 可能跨平台使用,如果用了 Windows API,换到了 Linux 平台部署就有了问题。

三、虚拟机
目前 Oracle 官方使用的是 HotSpot,它最早由一家名为 “Longview Technologies” 公司设计,使用了很多优秀的设计理念和出色的性能,1997年该公司被 SUN 公司收购。后来随着JDK-起发布了 HotSpotVM。目前 HotSpot 是最主要的 VM。

四、GC 垃圾收集器
堆内存里面经常创建、销毁对象,内存也是被使用、被释放。如果不妥善处理,一个使用频繁的进程,将可能有内存容量,但是无法分配出可用内存空间,因为没有连续成片的内存了,内存全是碎片化的数据。

  • 回收基本算法(java不使用)
    每一个堆内对象上都与一个私有引用计数器,记录着被引用的次数,引用计数清零,该对象所占用堆内存就可以被回收。循环引用的对象都无法引用计数归零,就无法清除。
  • 标记清除 Mar-Sweep
    分垃圾标记阶段和内存释放阶段。标记阶段,找到所有可访问对象打个标记。清理阶段,遍历整个堆,对未标记对象清理。
    在这里插入图片描述
    每一块表示使用的内存,需要判断是否使用,进行标记,清理未标记的内存,形成碎片化内存。其缺点就是会造成大量的内存碎片。
  • 复制 Copying
    先将可用内存分为大小相同两块区域A和B,每次只用其中一块,比如A。当A用完后,则将A中存活的对象复制到B。复制到B的时候连续的使用内存,最后将A一次性清除干净。
    在这里插入图片描述
    缺点是则损一半内存使用量,因为内存对半划分了,复制过程CPU也是有代价的。好处是没有碎片,复制过程中保证对象使用连续空间。
  • 标记-压缩 Mark-Compact
    分垃圾标记阶段和内存整理阶段。标记阶段,找到所有可访问对象打个标记。
    内存清理阶段时,整理时将对象向内存一端移动,整理后存活对象连续的集中在内存一端。
    在这里插入图片描述
  • 分代收集算法
    既然上述垃圾回收算法都有优缺点,能不能对不同数据进行区分管理,不同分区对数据实施不同回收策略,分而治之。

1.7及以前,堆内存分为新生代、老年代、持久代。

1.8开始,持久代没有了,取而代之 MetaSpace。

五、STW
对于大多数垃圾回收算法而言,GC 线程工作时,需要停止所有工作的线程,称为 Stop The World。GC 完成时,恢复其他工作线程运行。这也是 VM 运行中最头疼的问题。

  • 分代堆内存
    在这里插入图片描述

六、新生代回收
起始时,所有新建对象都出生在 eden,当 eden 满了,启动 GC。这个称为 Young GC,Minor GC。
先标记 eden 存活对象,然后将存活对象复制到from (假设本次是from,也可以是to,它们可以调换),eden 剩余所有空间都"清空"。GC 完成。
在这里插入图片描述

继续在eden创建新对象,当eden满了。启动GC,注意,此时会标记eden+from区域的存活对象,然后将存活对象复制到to。将 eden 和from"清空"。根据上面图例,假设 c 在from存活区已经为垃圾。视图如下:
在这里插入图片描述
继续新建对象,当 eden 满了,启动 GC。

先标记 eden 和 to 中存活对象,然后将存活对象复制到from。将 eden 和from"清空"。

以后就重复上面的步骤。

大多数对象都不会存活很久,而且创建活动非常多,新生代就需要频繁垃圾回收。
但是,如果一个对象一直存活,它最后就在 from、to 来回复制,如果 from 区中对象复制次数达到阈值,就直接复制到老年代。

七、老年代回收
进入老年代的数据较少,所以老年代区被占满的速度较慢,所以垃圾回收也不频繁。老年代GC称为 Old GC,Major GC。
由于老年代对象一般来说存活次数较长,所有较常采用标记-压缩算法。
此标记-压缩算法是要到一定程度才会执行。

Full GC:对所有“代”的内存进行垃圾回收

Minor GC 比较频繁,Major GC 较少。但一般 Major GC 时,由于老年代对象也可以引用新生代对象,所以先进行一次 Minor GC。然后在 Major GC 会提高效率。可以认为回收老年代的时候完成了一次 Full GC。

八、GC 触发条件
Minor GC 触发条件:当 eden 区满了触发

Full GC 触发条件:

·老年代满了(达到一定阈值就应该做压缩,不要等满了,那就太晚了!!)

·新生代搬向老年代,老年代空间不够

·持久代满了

·System.gc()手动调用。不推荐

九、调整策略

  • 源头上减少垃圾的生成

  • 减少 STW 时长,串行变并行

  • 减少 GC 次数,要分配合适的内存大小

  • 对 JVM 调整策略应用极广

    • 在 WEB 领域中 Tomcat 等

    • 在大数据领域 Hadoop 生态各组件

    • 在消息中间件领域的 Kafka 等

    • 在搜索引擎领域的 ElasticSearch.Solr 等

    • 在不同领域对 JVM 需要不同的调整策略

十、垃圾收集器类型
按回收线程数:

  • 串行垃圾回收器:一个 GC 线程完成回收工作

  • 并行垃圾回收器:多个 GC 线程同时一起完成回收工作,充分利用CPU 资源

指的是GC线程是否串并行
在这里插入图片描述

在这里插入图片描述

按工作模式不同:

  • 并发垃圾回收器:让 GC 线程垃圾回收某些阶段可以和工作线程一起进行。
  • 独占垃圾回收器:只有 GC 在工作,STW 一直进行到回收完毕,工作线程才能继续执行。
    指的是 GC 线程和工作线程是否一起运行。

一般情况下,我们大概可以使用以下原则:

  • 客户端或较小程序,内存使用量不大,可以使用串行回收;

  • 对于服务端大型计算,可以使用并行回收;

大型 WEB 应用,用户端不愿意等,尽量少的 STW,可以使用并发回收;

在这里插入图片描述

参数说明ju
-Xms设置应用程序初始使用的堆内存大小(新生代+老年代)-Xms2g
-Xmx设置应用程序能获得的最大堆,内存早期VM不建议超过32G,内存管理效率下降-Xms4g
-X:NewSize设置初始新生代大小
-XX:MaxNewSize设置最大新生代内存空间
-X:NewRatio以比例方式设置新生代和老年代-X:NewRatio=2
new/old=1/2
-XX:SurvivorRatio以比例方式设置eden和survivor-XX:SurvivorRatio=6
eden/survivor-6/1
survivor/new=1/8
-Xss设置线程的栈大小

接下来就可以进行测试调整

public class HelloWorld extends Thread{

    public static void main(String[] args) {
        try {
            while (true){
                 Thread.sleep(2000);
                 System.out.println("hello smallDrama");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

启动程序,去jdk\bin\jvisualvm.exe 双击启动如下:
在这里插入图片描述
发现最大空间给了4G,最小是分配了200多MB,实际可以看出不过10到20MB,我们可以调优分配 最小内存:128MB,最大内存:512MB; 输入命令 :java -Xms128m -Xmx512m HelloWorld 如下:
在这里插入图片描述
查看程序内存分配:
在这里插入图片描述
可以看到最大最小为我们最新分配的内存空间大小。

Tomcat调优
默认是不需要置顶内存的,-Xmx大约使用了1/4的内存。
在 tomcat 的bin/catalina.sh中增加

JAVA_OPTS="-server Xmx512m -Xms128m -XX:NewSize=48m -XX:MaxNewSize=200m"

-server:VM运行在server模式,为在服务器端最大化程序运行速度而优化。
-client: VM运行在Client模式,为客户端环境减少启动时间而优化。

启动观察:

十一、垃圾回收器
新生代

  • 新生代串行收集器:单线程、独占式串行,回收算法标记-复制。

  • 新生代并行收集器:将单线程的串行收集器变成了多线程并行、独占式。

  • 新生代并行回收收集器:多线程并行、独占式,使用复制算法,关注调整吞吐量。

老年代

  • 老年代串行收集器;单线程、独占式串行,回收算法使用标记-压缩。

  • 老年代并行回收收集器;多线程、独占式并行,回收算法使用标记-压缩,关注调整吞吐量。

  • CMS 收集器

    • Concurrent Mark Sweep 并发标记清除算法
    • 在某些阶段尽量使用和工作线程一起运行,减少停顿时长。是互联网站点服务端 BS 系统上较佳的回收算法。
    • 分为4个阶:初始标记、并发标记、重新标记、并发清除,在初始标记、重新标记时需要 STW。
  • G1 收集器

    • Garbage First 是最新垃圾回收器,从 DK1.6 实验性提供,JDK1.7发布,其设计目标是在多处理器、大内存服务器端提供优于 CMS 收集器的吞吐量和停顿控制的回收器。建议 JDK8 再考虑它。
    • 基于标记压缩算法。+UseG1GC。
    • 分为4个阶段:初始标记、并发标记、最终标记、筛选回收。并发标记并发执行,其它阶段 STW 只有 GC 线程并行执行。

十二、垃圾收集器设置
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值