什么是JVM
JVM是java virtual mechine 的缩写,是在真实的计算机基础上模拟计算机的各种功能的一种实现,JVM的本身对于计算机而言也是一个程序。
Java的跨平台性其实是依赖JVM,也就是在任何计算机平台安装JVM,就可以执行java的字节码文件,达到了"一处编译,处处运行"。
JVM的组成部分
JVM由五部分组成:堆、栈:(虚拟机栈、本地方法栈)、方法区、程序计数器。
image.png
堆:根据对象的声明周期不同,划分不一样的区域:新生代、老年代、永久代(1.8之后被元空间取代)。新生代存放的一些朝生夕死的对象,老年代存放一些大对象或者多次GC后还存在的对象,永久代存放一些类信息、常量、静态变量等数据。这样划分的目的,是为了更好的回收内存,对于一些需要频繁回收,声明周期短的对象,放在新生代中。
其中Eden区和From Survivor区、To Survivor区比例8:1:1,GC开始时对象只会存在Eden和From区,To区是空的。GC进行时,Eden区存活的对象会被复制到To区,From区会根据对象的年龄来决定去向,默认15,超过会将From区移到老年代,否则移到To 区。 接着会将Eden和From 区清空,然后将To 区域的对象重新复制到新的From区域。其中如果To区没有足够的空间来保存上次新生代存活下的对象时,有老年代担保将这些对象放在老年代中。
虚拟机栈:是用于描述java方法执行的内存模型。每个方法在执行的时候都会创建一个栈帧,栈帧的组成:局部变量表、操作数栈、动态连接、方法出口。每个方法执行的过程就是一个压栈和出栈的过程。
每个线程有独自的虚拟机栈,线程之间隔离。若单个线程请求栈的深度大于虚拟机允许的深度,会抛出StackOverFlowError。
本地方法栈 :本地方法栈于虚拟机栈类似,不同的是调用的native方法。
方法区:类的信息、常量、静态变量等信息,1.7开始,字符串常量池移到java heap中,1.8 开始元空间取代永久代。方法区只是一种规范,一开始HotSpot只是用永久代来实现方法区,能够省去这块内存回收的逻辑。但是永久代受-XX:MaxPermize大小限制,会出现OOM。
程序计数器:较小的一块空间,主要是记录程序执行字节码的行号指示器。对于Java的多线程而言,是通过切换分配处理器执行时间来实现的,所以为了线程切换后能够恢复到正确的位置,每条线程都有一个程序计数器。
垃圾收集器和内存分配策略
对于Java来说,程序计数器、虚拟机栈、本地方法栈这些是伴随着线程一块销毁,而堆和方法区是需要回收的,针对这块内存JVM如何分配,如何收入,下面就来一块看下。
首先,先搞明白这三个问题:
- 哪些内存需要回收?
- 什么时候回收?
- 如何回收
- 哪些内存需要回收?
堆和方法区,最主要的就是堆,每次GC就会回收堆里70%-80%左右内存。堆里主要存放的就是对象和常量池,所以先要判断哪些对象和方法区是可以回收的。
判断对象已死的方式:
- 引用计数法
如果对象被引用+1,引用失效-1,当值为0时对象可回收,这种方式的缺陷在于如果两个对象互相引用,比如: a=b; b=a; a=null; b=null; 这时对象引用不为0,事实上a,b两个对象都可以回收的。 -
可达性算法分析
以“GC Root” 为起点,往下如果引用链中没有该对象,则回收该对象,即使被finalize的对象,也是有可能逃离回收的命运,真正的回收是需要经过两次标记,当第一次finalize的时候,会将对象发在F-Queue队列中,会对队列再次执行finalize这时对象如果被引用就会逃离被回收的命运。
image.png
GC Root的对象:虚拟机栈的局部变量表的所有对象;方法区静态属性的所有对象;方法区常量的所有对象;Native方法的所有对象。
- 引用类型:强、弱、软、虚
只是用做标记对象的状态,强引用:抛出OOM也不会回收 ;弱引用:在抛出OOM之前会尝试回收弱引用的对用,多用于图片和网站缓存;软引用:存活在下次GC之前;虚引用:没有实际应用场景,随时可以被回收。
-
什么时候回收?
对象在被标记不可达对象时,也不会立即被回收,回收的动作是发生在对象分配空间不足时,才会真正的执行回收动作。 -
如何回收?
针对不同代回收的机制不同,新生代都是朝生夕死的对象使用的是复制算法,将Eden区和From区没有被回收的区域复制到To区,然后将Eden和From清空,在将To区的对象复制到From区。
老年代的回收方式,对于一些大对象考虑内存的问题,适用标记清除和标记整理。
工具和相关参数配置
java自带的命令
- jps 列出虚拟机进程
- jstat 虚拟机监视信息统计工具
- jmap java某个进程所有对象的信息
- jstack java堆栈跟踪工具
图形化工具 jconsole
参数配置
打印trace信息
参数 | 描述 |
---|---|
-XX:+PrintGC或者-verbose:gc | 打印GC日志 |
-XX:+PrintGCDetails | GC详细日志 |
-XX:+PrintGCTimeStamps | gc的时间戳 |
-Xloggc:/log/gc.log | gc文件的目录 |
-XX:+PrintHeapAtGC | 打印GC前后堆的日志 |
-XX:TraceClassLoading | 监控类的加载 |
堆的分配参数
堆的内存= 年轻代+老年代+永久代
永久代固定值64m,年轻代建议占3/8。
参数 | 描述 |
---|---|
-Xmx,-Xms | 堆的最大值和最小值,开发过程中经常配置为一样的,是为了GC后不需要重新分配造成资源浪费 |
-XX:+HeapDumpOnOutOfMemoryError、-XX:+HeapDumpPath
查看系统dump的时候发生什么,导出OOM的路径。
网友评论