JVM?看这一篇还不够吗
写在前面
随着我们程序员‘入坑’的时间越来越长,不可避免地遇到JVM方面的问题,比如各种OOM异常、GC次数过多、每次GC时间很长等等问题。这时候,不掌握JVM知识,遇到GC问题,一脸懵逼啊,还怎么去调优!同时,JVM知识在出去找工作面试时,几乎也是被面试官必问的知识点,所以,不掌握这玩意,被面试官问到,又是一脸懵逼啊。本章就是专门用来详细介绍JVM的,尽力简单高效的分享知识。
内容简介
1 | 复制代码一 JAVA内存模型的介绍 |
一 Java内存模型
1 定义:
1 | erlang复制代码 描述的是一组规范,定义了变量的访问方式. |
2 特性
1
复制代码 可见性: 当一个线程对共享变量做了修改后其他线程可以立即感知到该共享变量的改变
1
复制代码 原子性: 是不可分割,当某个线程在做某个业务时,要么同时成功,要么失败,中间不能被加塞或分割。
1
复制代码 有序性: 计算机在执行程序时,为了提高性能,编译器和处理器常常会做指令重排。
3 Volatile关键字
3.1 特性:
- 保证可见性
- 不保证原子性
- 禁止指令重排序优化。(保证了JMM中的有序性)
若用volatile修饰共享变量,在编译时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
在虚拟机层面,为了尽可能减少内存操作速度远慢于CPU运行速度所带来的CPU空置的影响,会修改代码的执行顺序。
3.2 原理
- 当写一个volatile变量时,把该线程对应的本地内存中的共享变量刷新到主内存。
- 当读一个volatile变量时,线程接下来将从主内存中读取共享变量。
3.3 如何解决原子性问题?
- 使用JUC包下原子类,比如AtomicInteger
- 加锁synchronized
二 JVM内部组成
1 类加载器子系统
类加载器主要分类有:
1 | bash复制代码引导类加载器:使用C/C++编写的,加载Java的核心类库,并不继承ClassLoader,没有父加载器 |
类加载过程:
1 加载
1 | 复制代码 通过一个类的全限定名来获取其定义的二进制字节流 |
2 链接
1 | vbnet复制代码 验证:确保被加载类的正确性,但不是必须的,它对程序运行期没有影响 |
符号引用:就是一组符号来描述目标,可以是任何字面量。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
3 初始化
1 | markdown复制代码只有对类的主动使用才会导致类的初始化,就是执行类构造器方法<clinit> 的过程 |
2 运行时数据区
栈:
1 | css复制代码生命周期:主管程序运行,在线程创建是创建,跟随线程的生命周期,线程结束栈内存释放。 |
栈帧内部结构:
1 | markdown复制代码 局部变量表 |
堆:
内部结构
1 | markdown复制代码新生代:占整个堆内存的1/3 |
方法区(jdk1.8叫元空间)
内部存储这些数据:
1 | markdown复制代码运行时常量池 |
本地方法栈
1 | 复制代码管理本地方法的调用 |
程序计数器
1 | erlang复制代码每个线程都有一个程序计算器,就是一个指针,指向方法区中的方法字节码(下一个将要执行的指令代码),由执行引擎读取下一条指令. |
3 执行引擎
1 | 复制代码任务:将字节码指令解释/编译为对应平台上的本地机器指令 |
4 本地方法接口
1 | java复制代码native 修饰的方法,由C/C++ 实现 |
三 JAVA对象内存布局和创建过程
1 内存布局
1 | markdown复制代码一 对象头 |
2 创建过程:
1 | markdown复制代码1 加载类元信息 |
四 JVM 参数调优
1 参数分类
1 | ruby复制代码1 标配参数 |
2 常用参数
1 | diff复制代码-Xms :初始内存大小,默认物理内存的1/64 |
结合SpringBoot:Java -server jvm各种参数 -jar jar/war包名称
五 垃圾回收
1 如何判断对象是否被回收?
1 | less复制代码引用计数算法: |
2 引用关系
1 | markdown复制代码强引用:只要强引用关系还存在,永远不会被JVM回收 |
3 GC算法
1 | erlang复制代码1 复制算法: |
JVM采用的分代收集算法
原因:因为不同对象的生命周期是不一样的新生代:采用了GC的复制算法,因为新生代一般是新对象,对象存活率低。
老年代:采用标清或标整。因为老年代中因为对象存活率高。
4 GC时可能发生的错误:
有两大类:
1 | ruby复制代码内存溢出: |
STW: (Stop-The-World)
是在执行垃圾收集算法时,应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互;这些现象多半是由于gc引起。
所有GC都会存在这个事件.
JVM在后台会自动发起和自动完成的.
Minor GC和Full GC的区别
1 | markdown复制代码Minor GC:又称新生代GC,指发生在新生代的垃圾收集动作; |
5 垃圾回收器
分类:
1 | arduino复制代码1 Serial :串行垃圾回收器,只有一个线程进行垃圾回收,会暂停用户线程,直到完成。 |
1 | objectivec复制代码2 Parallel: 并行垃圾回收器,多个垃圾收集器线程并行工作,java8默认 |
1 | objectivec复制代码3 CMS:并发垃圾回收器 |
1 | objectivec复制代码 为何不使用标记压缩算法? |
吞吐量与收集器关注点说明
吞吐量(Throughput)
CPU用于运行用户代码的时间与CPU总消耗时间的比值; 即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间); 高吞吐量即减少垃圾收集时间,让用户代码获得更长的运行时间;
垃圾收集器期望的目标(关注点)
1 停顿时间
停顿时间越短就适合需要与用户交互的程序; 良好的响应速度能提升用户体验;
2 吞吐量
高吞吐量则可以高效率地利用CPU时间,尽快完成运算的任务; 主要适合在后台计算而不需要太多交互的任务;
3 覆盖区(Footprint)
在达到前面两个目标的情况下,尽量减少堆的内存空间; 可以获得更好的空间局部性;
六 性能分析
常用命令:
1 | lua复制代码1 Jps: 列出系S统中所有java应用程序。 |
未完待续。。。
本文转载自: 掘金