JVM详细解析(Java 8 HotSpot VM内存分配、GC)
1 简介
JVM是Java虚拟机的简称,是为了保证Java程序能运行在不同操作系统上的一个虚拟机器,它不直接与硬件交互,而是运行在操作系统上,所有的Java线程都会调用操作系统的线程,通常是C线程,所有的系统线程均通过CPU进行管理与分配。
2 JVM角色
- 虚拟机Stack
- 本地方法栈
- 程序计数器
- 堆
- 方法区
- 类加载器
- 执行引擎
2.1 线程私有内存
线程的私有内存包括虚拟机栈、本地方法栈、程序计数器。
- 虚拟机栈通常是在线程启动之前被创建,用来存储方法、局部变量、8种基本数据类型、操作状态(出、入栈)、方法动态链接,方法出口,调用一个方法,伴随着方法的入栈,结束方法,伴随着出栈,每个方法在栈中都意味着一个栈帧,他与线程同声明周期。
- 本地方法栈,与虚拟机栈的功能相当,只不过存储的是本地方法的相关内容,native 方法通常是用来与硬件交互的,现在已经很少使用了,通常只出现在用Java驱动硬件的场景,如打印机等等。
- 程序计数器,这个程序计数器是用来保存线程下一个执行链接的,是线程运行字节码的行号计数器,用来表示线程执行的进度,这里是不会发生OOM的。
2.2 线程共享区域
线程共享区域主要包括堆、元空间(方法区)
- 堆,这是存储对象实例的地方,是GC的主要区域,由于Java的GC属于分代回收,所以堆可以按照**(1:2)[young:old]**,同时young又按照(8:1:1)[eden,s-from,s-to]进行内存分配。
- 之前叫方法区,主要存储(常量、静态变量、类信息、运行时常量池),但是后来改成MetaSpace后将运行时常量池划、静态变量、常量分到堆的一部分,同时Class类信息被分到了native内存,方法区这里基本不会发生GC,就算GC也只是简单的常量回收以及类的卸载,收益很小。
2.3 类加载器(双亲委派机制)
ClassLoader叫做类加载器,通常分为自带的类加载器
- 启动类加载器
- 扩展类加载器
- 系统类加载器(应用程序加载器)
- 用户自定义加载器
类加载器遵循双亲委派机制
,通常当一个类来了之后,不会立马交给当前类加载器加载,首先优先交给父类加载器进行尝试加载,如果在该加载器的类路径下加载不到该类,那么就交给下级进行加载。
rt.jar是java运行时的核心jar包,里面包含lang,util等核心包,通常java.lang.Object这个类最终会交给启动类加载器进行加载,这样的好处是不管使用哪个加载器加载该类,最终的Object类都是一致的。
3 java GC
首先Java GC主要发生在Heap堆共享内存中,之前讲过堆实际上是分为如下几层:
- young:1
- eden:8
- survivor_from :1
- survivor_to :1
- old :2
注::后面为内存空间所分配的比例
3.1 minor GC
这是发生在young区域的GC,通常GC算法使用复制算法
,简单的流程为复制-清除-替换
- 将eden与survivor from区域的可回收对象进行标记,将不可回收的对象全部复制survivor to区域,同时将年龄+1,如果survivor to的空间不够,那么将数据移动到old区域。
- 然后将标记需要被清除的数据进行清除。
- 将survivor from 与survivor to进行位置的交换。
3.2 full GC(major GC)
major GC主要发生在老年代,通常是old区域满了之后才开始进行。
如果在FullGC完成之后依然无法进行对象的保存,那么就会产生OOM(java.lang.OutOfMemoryError: java heap space 异常)
!通常有以下原因。
- Java虚拟机分配的堆内存不够,可以通过 -Xms -Xmx进行堆内存的设置。
- 代码中创建了大量的对象,且这些对象不能被垃圾收集器回收,仍然处于被引用的状态。
4 常用的JVM调优
4.1 Java 7
- -Xms :最小Heap内存设置
- -Xmx:最大heap内存设置
- -Xmn:young区域内存设置
- -XX:PermSize:永久代(方法区)内存设置
- -XX:MaxPermSize:永久代(方法区)最大内存设置
4.2 Java 8
- -Xms :最小Heap内存设置,默认为物理内存的1/64.
- -Xmx:最大heap内存设置,默认为物理内存的1/4.
- -Xmn:young区域内存设置
在Java8中,永久代(方法区)已经被移除,被一个叫做MetaSpace
的区域替代,两者本质相同,只不过,MetaSpace已经不属于JVM的内存区域,而是本地内存的一部分,受操作系统控制,也被称为non-heap内存,从此JVM能加载多少类的元数据就不受PermSize的控制了。
4.3 常用的VM参数示例
-Xms1024m -xmx1024m -XX:PrintGcDetails #很多Java编写的组件,框架都有对用的JVM调优以及GC优化