JVM学习
1.1 JVM 种类
重用HOTSPOT。还有些列入 IBM开发的灯。
1.4 JVM和操作系统的关系
1.5 JVM JRE JDK
JDK -> JRE -> JVM
开发需要JDK,运行需要JRE
1.6 JVM虚拟机规范
JVM是栈结构。JVM翻译字节码有两种方式,解释执行,还有一种是JIT 。
1.7 JVM整体架构
init 改为 initiation. 先执行 cinit (静态变量,静态方法),再为 init(非静态类成员,接口)。
linking 。
- verify,验证字节码有没有危险(验证格式,元数据,字节码,符号引用)。
- prepare。给静态变量赋初值 比如int 给个0, final 的直接赋值。初始化(成员变量不会在这赋值,init才给出始化值)
- resolve。将符号引用换成直接引用。(直接引用,可以直接链接),解析阶段则把这个类激活了
pc registers |
占用内存小,线程私有 thread-specific data |
大致为字节码(byte code)行号 | 靠线程里面的程序计数器。因此可以找到下一条执行指令。每个线程有一个独立的计数器。因此不同线程切换执行可以正常执行 | |
VM stack area | 线程私有,空间连续 continuous space | 局部变量表,操作栈,动态链接、方法返回地址等 | -xss | |
heap area | 线程共享,生命周期跟JVM相同,空间地址可以不连续 | 保存对象的实例, 所有对象实例都要在堆上分配 |
-Xms -Xsx -Xmn |
(JDK 8) |
method area (meta area) |
线程共享,生命周期跟JVM相同,空间地址可以不连续 |
存储已被虚拟机加载的类信息,常量,静态变量。即时编译器编译 后的代码等数据 |
||
native method area | 线程私有 | 为链接native方法使用 |
1.8 JVM运行时内存介绍
1)runtime area JDK8
JDK 8 中,method area 换成了 meta space。meta space 和 direct memory 放在了jdk意外的地方,单独有一块内存
2)JDK8 VS JDK7 Runtime Area
JDK7 |
|
JDK8 |
|
3)JDK7 VS JDK8一些问题
- JDK8移除了permgen,替换为 metaspace
- 永久代中的class meta转移到了 native memory
- 永久代的interned Strings 和 class native variables 转移到了 java heap
4) 为什么要把permgen 换成 metaspace
- 字符串在永久代中,容易出现性能问题和内存溢出
- 类及方法信息等比较难确定大小,对永久代的大小指定比较难,太小容易出现永久代溢出,太大容易出现老年代溢出
- 永久代GC带来不必要的复杂度,回收效率低
- hotspot 与 jrockit 可能合并,jrockit没有永久代
1.10 虚拟机栈
每个方法执行时候都有一个栈帧。
Local Variable Table |
|
Operand Stack |
|
dynamic linking |
|
return address |
|
1.16 本地方法栈
为虚拟机使用的本地方法服务。
- native 调用非java语言服务。
- 线程私有
1.17 堆
JVM里面最大一块区域,堆里面基本存放所有对象(有些对象可以存放在方法区)
- 最大一块区域
- 线程共享 (Thread Local Allocation Buffer 是私有的)
- 虚拟机启动时候创建
- 存放对象实例
- 垃圾回收的主要区域
- 现在的垃圾回收算法基本为分代回收算法
- java堆在物理存储是不连续的,逻辑是连续的
- 方法结束后对象不会立马失效,等GC来回收
1)设置大小
- 内存大小 -Xmx 内存最大值, -Xms 内存最小值
2) 堆的分类
JDK7 |
|
|
JDK8 |
|
|
1)年轻代与老年代
- 年轻代大小一般是1,老年代大小一般是2
- Eden ,survivor0,survior1 默认为 8:1:1
-XX:NewRatio Old = 2, Old : New。
-XX:SurvivorRation = 8 Eden : Young.
-xmn young
1.21 对象分配过程
- new 在Eden创建
- Eden 满了会GC。MinorGC (整个young 都能清理)
- 触发GC Eden剩余放入S0 年龄+1, 如果S0满了,放入S1,年龄+1
- 触发GC S0没回收的放入S1,垃圾的年龄+1
- 触发GC S1 再放入S0, 当年龄达到15,放入old. --XX:MaxTenuringThreshold = N
- 老年代内存不足,GC: Major GC. OutOfMemory, 当老年代也没法保存
1.22 GC
minor GC | 新生代 |
|
Major GC | 老年代 |
|
Mixed GC | 混合 | |
Full GC |
全部回收 |
|
1.24 元空间
- 类的元信息放入metaspace,静态变量和常量放入堆中。以前(元信息,静态变量、常量放入方法区)
- 元数据现在在本地空间则可以设置比较大。
- 也不能无限大
- --MetaspaceSize 初始空间大小
永久代 (元信息,静态变量,常量)内容太大,不好分配内存,GC会增加复杂度。
1.24 方法区
- 线程共享,存运行时候常量池,类信息
- 元空间和永久代是方法去的落地实现
- .class 放入方法区 引用放入栈,new 对象放入堆
1.25 常量池
是class文件中。引用对应了相关信息
1.26 直接内存
操作本地内存.
1.38 初始化
类的初始化是类加载的最后一个步骤。用cinit类构造方法,javac自动生成。初始化静态变量,静态代码块赋值。父类的cinit先执行
// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`, // then press Enter. You can now see whitespace characters in your code. public class Main { static { i = 1; // System.out.println(i); 报错 } static int i = 2; }
- 先prepare i = 0 赋值
- 再 i = 1;
- System.out.println(i) 报错。
- 最后再 i = 2;
接口中不执行cinit方法。
cinit 优于 init
1.39 类加载器
1)Bootstrap 加载资源,2)自己定义 加载路径,自定义等,例如不同的类加载到不同的应用需要写自定义类加载器。
1.48 垃圾回收
1) 垃圾回收算法
引用计数法 (判断对象生死) reference counting |
被引用的时候计数器+1,引用失效时候计数器-1 |
优:
缺:
object1.obj = object.2 object2.obj = object.1
object1 = null; object2 = null; |
可达性算法 (判断对象生死) recheability analysis |
从GC Root开始,看引用链。如果对象不再被引用,则为垃圾。 可作为GC Root的是,
真正宣告一个对象死亡,要经过两次标记,然后会被一个低优先级线程Finalizer执行。所以finalize不会立刻执行
|
|
分代收集算法(严格来说是一种理论) |
|
|
标记清除法 (回收算法,其他垃圾回收算法的基础) mark and sweep |
两种
从GC ROOT开始查找引用链,没有被引用的标记一下。 缺:
|
|
标记复制法 (回收算法) mark and copy |
过程:
98% 的垃圾在 Eden被销毁,然后把Eden存活的和S0 或者S1 存活的放入 S1 或者S0,因为存活对象少,因此需要的保留区域小,所以在Young Generation中可以使用标记复制法。
缺:
|
|
标记整理算法 (垃圾回收算法) mark-compact algorithm |
一开始和标记清除一样,只不过会把存活对象按照顺序依次移动成连续空间。
缺点: 移动对象复杂度高,停顿时间长。
优: 从程序吞吐量来看,移动对象更划算, |
|
1.52 对象的引用
强引用 | A a = new A(); | 不会靠回收来解决内存不足的问题。指的是会抛出OutOfMemory |
软引用 | String str = new String( "abc" ); // 强引用
SoftReference<String> softRef = new SoftReference<String>(str); 当需要构造一些占用内存比较大的对象使用
|
内存不足时候会回收,内存足时候不会回收 |
弱引用 | String str = new String( "abc" ); //强引用
WeakReference<String> weakRef = new WeakReference<String>(str); // 弱引用。当需要构造一些占用内存比较大的对象使用 |
下一次GC时候会被回收,不管内存是否足够 |
虚引用 | PhantomReference | 一定要与ReferenceQueue联合使用,肯定会被加入到队列中,用来跟踪对象被垃圾回收的过程,不决定对象的生死,只有当对象被回收的时候,如果发现该对象还有虚引用,会被加入到ReferenceQueue中 |
1.57 垃圾回收器
重点是 CMS,G1.
Serial GC |
-XX: +UseSerialGC 年轻代、老年代都会是 Serial |
会有Stop the world,暂停用户线程
|
Parallel GC |
-XX: +ParNew
|
回收年轻代是多线程的Serial 版本,其他一样 看CPU是否多核,来选择
服务器用的多,大数据平台等, JDK 8 默认用 Parallel Young, Parallel Old。 JDK 9 默认G1 比较主流 JDK 14 移除了 CMS,弃用 Para young Para Old |
Parallel Scavenge | -XX: |
回收年轻代,多线程,复制算法,以吞吐量优先 自适应调节策略,动态改变 Eden,S0,S1 比例 适合后台运算,交互不多的任务,例如订单处理、科学计算 |
CMS GC | JDP 14废除 |
停顿时间短,在交互多的应用上用效果比较好
并发清除有空间碎片问题。 三色标记
白-> 灰 -> 黑
缺点:
|
G1 GC | -XX:+UseG1GC |
多核CPU,大容量内存服务器 用G1,比较主流
|
吞吐量:用户时间 / (用户时间 + 垃圾回收时间)
1.60 Parallel Scavenge 收集器
Parallel