这些 JVM 的知识,足可以让你入门虚拟机调优了!

理解JVM

理解JVM主要从以下三个方面进行:

  • Java代码的执行

  • 内存管理

  • 线程资源同步和交互机制

JVM内部结构以及类加载机制

JVM内部结构

在这里插入图片描述

JVM类加载机制

在这里插入图片描述

ClassLoader继承关系

在这里插入图片描述

1.Bootstrap ClassLoader

使用C++实现

2.Extendsion ClassLoader

3.System ClassLoader

4.User-Defined ClassLoader

是开发人员继承ClassLoader抽象类自行实现的ClassLoader
可用于加载非classpath中(例如从网络上下载的jar或二进制)的jar以及目录
可以在加载之前对class文件做一些动作,例如解密

栈的体系结构

Sun JDK基于栈的体系结构来执行字节码,基于栈方式的好处为代码紧凑,体积小

线程在创建后,都会产生程序计数器(PC)和栈(Stack);PC存放了下一条要执行的指令在方法内的偏移量;栈中存放了栈帧,每个方法调用都会产生栈帧

栈帧主要分为局部变量区操作数栈两部分,局部变量区用于存放方法中的局部变量和参数,操作数栈中用于存放方法执行过程中产生的中间结果

在这里插入图片描述

JVM内存结构

在这里插入图片描述

方法区

方法区存放了要加载的类的信息、类中的静态变量、类中定义为 final 类型的常量、类中的 Field 信息、类中的方法信息
方法区是全局共享的,在一定条件下会被 GC,当方法区要使用的内存超过其允许的大小时,会抛出 OutOfMemory
在 SunJDK 中,方法区对应 Permanet Generation,又称为持久代,可以通过 -XX:PermSize 及 -XX:MaxPermSize 来设置

堆用于存放对象实例和数组值,Heap中对象所占用的内存由GC进行回收
32bit最大内存为2G,64bit无上限
通过设置-Xms -Xmx来指定堆的大小,避免频繁调整Heap大小,造成堆的震荡

堆的分代:

在这里插入图片描述

1.新生代(New Generation)

大多数情况下,Java程序中新建的对象都从新生代分配内存,新生代有Eden Space和两块相同大小的Survivor Space(通常又称为S0和S1或From和To)构成,可通过**-Xmn**参数来指定新生代的大小,也可以通过-XX:SurvivorRatio来调整Eden Space和Survivor Space,有些GC方式还会根据运行状况来调整Eden、S0、S1的大小

2.旧生代(Old Generation / Tenuring Generation)

用于存放新生代中经过多次垃圾回收仍然存活的对象,例如缓存对象,新建的对象也有可能在旧生代上直接分配内存。主要有两种情况(由不同的GC实现来决定):一种为大对象,可通过在启动参数上设置 -XX:PretenureSizeThreshold=1024 来代表当对象超过多大时就不在新生代分配,而是直接在旧生代分配,此参数在新生代采用 Parallel Scavenge GC 时无效,Parallel Scavenge GC会根据运行状况决定什么对象直接在旧生代上分配内存;另一种为大的数组对象,且数组中无引用外部对象

旧生代所占用的内存为 -Xmx对应的值减去 -Xmn对应的值

本地方法栈

本地方法栈用于支持native并发的执行,存储了每个native方法调用的状态,在Sun JDK的实现中本地方法栈和JVM方法栈是同一个

PC寄存器和JVM方法栈

每个线程均会创建PC寄存器和JVM方法栈,PC寄存器占用的可能为CPU寄存器或操作系统内存,JVM方法栈占用的是操作系统内存,为线程私有,其在内存上分配非常高效。当方法运行完毕时,其对应的栈帧所占用的内存也会自动释放

当JVM方法栈空间不足时,会抛出StackOverflow的错误,在SunJDK中可以通过**-Xss**来指定其大小

内存管理

内存分配(TLAB)

Java对象所占用的内存主要从堆上进行分配,堆是所有线程共享的,因此在堆上分配时需要进行加锁,这导致了创建对象开销比较大。当堆上空间不足时,会触发GC,如果GC后空间仍然不足,则抛出OutOfMemory错误信息

SunJDK为了提升内存分配的效率,会为每个新创建的线程在新生代的Eden Space上分配一块独立的空间,这块空间成为TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行情况计算而得,可通过-XX:TLABWasteTargetPercent来设置TLAB可占用的Eden Space的百分比,默认值为1%。JVM根据这个比率、线程数量、及线程是否频繁分配对象来给每个线程分配合适大小的TLAB空间。

在TLAB上分配内存时不需要加锁,因此JVM在给线程中的对象分配内存时会尽量在TLAB上分配,如果对象过大或TLAB空间已经用完,则仍然在堆上进行分配

因此在编写Java程序时,通过多个小对象比大的对象分配起来更加高效

-XX:+PrintTLAB来查看情况

内存回收

引用计数器(SunJDK未采用)
跟踪收集器

跟踪收集器采用的为集中式的管理方式,全局记录数据的引用状态。基于一定条件的触发(定时,空间不足),执行时需要从根集合来扫描对象的引用关系,这可能会导致应用程序暂停,主要有复制(Copying)、标记-清除(Mark-Sweep)和标记-压缩(Mark-Compact)三种实现算法

根对象(GC Root)
在这里插入图片描述

复制(Copying)

复制采用的方式为从根集合扫描出存活的对象,并将找到的存活对象复制到一块新的完全未使用的空间中;当要回收的空间中存活对象较少时,复制算法会比较高效,其带来的成本是要增加一块空的内存空间及进行对象的移动

标记-清除(Mark-Sweep)

从根对象集合开始扫描,对存活的对象进行标记,标记完毕后,在扫描整个空间中为标记的对象,进行回收

标记清除动作不需要进行对象的移动,且仅对其不存活的对象进行处理。在空间中存活对象较多的情况下较为高效,但由于标记-清除采用的为直接回收不存活对象所占用的内存,因此会造成内存碎片

标记-压缩(Mark-Compact)

标记-压缩采用和标记清除一样的方式对存活的对象进行标记,但在清除时则不同,在回收不存活对象所占用的内存空间后,会将其他所有存活对象都往左端空闲的空间进行移动,并更新引用其对象的指针

标记-压缩在标记-清除的基础上还须进行对象的移动,成本相对更高,好处则是不产生内存碎片

SunJDK中可用的GC

在这里插入图片描述

对象的引用关系

1.强引用(StrongReference)

强引用的对象直到主动释放引用才会被GC

2.软引用(SoftReference)

当JVM内存不足时会被收回

3.弱引用(WeakReference)

采用弱引用建立引用的对象没有强引用后,GC时即会被自动释放

4.虚引用(PhantomReference)

可以更跟踪对象是否已经从内存中被删除

PhantomReference 类只能用于跟踪对被引用对象即将进行的收集。同样,它还能用于执行 pre-mortem 清除操作。 PhantomReference 必须与 ReferenceQueue 类一起使用。需要 ReferenceQueue 是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时, PhantomReference 对象就被放在它的 ReferenceQueue 上。将 PhantomReference 对象放在 ReferenceQueue 上也就是一个通知,表明 PhantomReference 对象引用的对象已经结束,可供收集了。这使您能够刚好在对象占用的内存被回收之前采取行动。

重点需要研究CMS和G1垃圾收集器

由于这些收集器的参数居多,很难进行系统的记忆,在进行调优的时候,只能借助于文档

这里简要描述一下CMS和G1的特性

CMS

为了解决Mark-Sweep方式对整个空间中的对象进行扫描并标记,这个过程会造成长时间的应用暂停

主要步骤:

  1. 第一次标记(Initial Marking),也就是俗称着色标记,需要暂停应用

  2. 并发标记(Concurrent Marking),对上一步着色的对象进行轮询

  3. 重新标记(Final Marking(remark)),重新着色

  4. 并发收集(Concurrent Sweeping),负责将没有进行标记的对象进行收集

G1

目标是做到尽量减少GC所导致的应用暂停时间,同时保证JVM堆空间的利用率

JVM内存状况查看方法和分析工具

GC输出参数

-XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime

常用工具

JConsole、JVisualVM、JMap、JHat、JStat、JStack、Eclipse Memory Analyzer

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GettingReal

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值