JVM的探究
面试问题
- 请您谈谈你对jvm的理解?
- Java8虚拟机和之前的更新变化?
- 什么是OOM ,什么是栈溢出?
- JVM的调优参数有哪些?
- 内存快照如何抓取,怎么分析Dump文件?
- 谈谈JVM中,类加载器的认识?
JVM的理解
JVM的位置
JVM的体系结构
类加载器
作用:加载class文件
类加载器的类型
- 应用程序加载器
- 扩展加载器
- 根加载器 Java程序无法调用,会输出null值
不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。
native关键字
带有native关键字的,说明Java的调用范围达不到,要去底层调用C语言的代码。
进入本地方法,调用本地方法的接口(JNI)。
JNI的作用:融合不同的语言进入Java中去。
为什么要又native关键字
- Java诞生的时候,c和c++比较流行,Java要想活下去,必须拥有调用c和c++的方法
- 它在内存程序中开辟了本地方发栈,登记native方法,最终驱动程序的时候,加载本地方法库中的方法去调用JNI。
PC寄存器
每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区的方法字节码。在程序中占用非常小的内存,几乎可以忽略不计。
方法区
方法区被所有线程所共享的,构造函数和接口代码等也在此定义。一般放置以下东西
- 静态常量
- 常量
- 类信息(接口定义,构造方法)
- 运行时的常量池
栈
栈:栈内存,主程序的运行,生命周期和线程同步。
线程结束,占内存也是释放,对于栈来说,不存在垃圾回收的问题。
一旦线程结束。栈就over。
栈里面存的数据:8大基本类型+对象引用+实例方法。
栈满了:StackOverFlowError
栈+堆+方法区:交互关系
JVM种类
- hotspot
- BEA
- IBM
堆
一个jvm只有一个堆,堆内存的大小是可以调节的。
类加载器读取类文件后,类,方法,常量,变量这些,而已大部分保存我们的引用类型的真是对象。
堆的分类:
- 新生区(伊甸园区)
- 类的诞生,成长,甚至死亡的区域
- 养老区
- 永久区(元空间)-------------方法区在这个区域------方法区里面有常量池
(逻辑上存在物理上不存在)原因:堆内存=新生区+养老区就满了。
- 这个区域是常驻内存。用来存放Java自身携带的class对象。存储的为Java运行时的一些环境或者类信息。这个区域不存在垃圾回收。关闭JVM时会释放这个区域的内存。
- 当大量的第三方jar被加载时,或者当tomcat部署太多应用,大量的反射类,不断的被加载。直到内存满了,就会出现OOM;
- 当出现OOM时怎么解决
1. 尝试扩大内存。用Xms和Xmx调节堆内存大小
2. 分析内存。查找问题
GC垃圾回收,主要在新生区和老年区。
OOM 堆内存不够 OutOfMemoryError;Java heap space
堆内存调优
在一个项目中,突然出现了OOM故障,那么该如何排除 研究为什么出错~
- 能够看到代码第几行出错:内存快照分析工具,MAT, Jprofiler
- Dubug, 一行行分析代码!
MAT, Jprofiler作用
- 分析Dump内存文件,快速定位内存泄露;
- 获得堆中的数据
- 获得大的对象~
MAT是eclipse集成使用 在这里不学
Jprofile使用
1.在idea中下载jprofile插件 2.联网下载jprofile客户端 3.在idea中VM参数中写参数 -Xms1m -Xmx8m -XX: +HeapDumpOnOutOfMemoryError 4.运行程序后在jprofile客户端中打开找到错误 告诉哪个位置报错 命令参数详解 // -Xms设置初始化内存分配大小/164 // -Xmx设置最大分配内存,默以1/4 // -XX: +PrintGCDetails // 打印GC垃圾回收信息 // -XX: +HeapDumpOnOutOfMemoryError //oom DUMP
GC
JVM在进行GC时,并不是对这三个区域统一回收。
大部分时候,回收都是新生代-- 新生代 ,幸存区(form,to)
老年区 GC两种类:轻GC (普通的GC), 重GC (全局GC)
堆里面的分区有哪些?
Eden, form, to, 老年区
GC的算法有哪些?
标记清除法
标记压缩清除算法
复制算法
引用计数器
轻GC和重GC分别在什么时候发生?
轻GC一般发生在新生区。一般用到的算法是复制算法。
重GC一般作用于老年区,一般用标记清除算法和标记压缩算法混合使用。
引用计数器:
引用计数法 很少使用了
##复制算法
每次将伊甸园区和一个幸存区的对象复制到另外一个幸存区,然后清空伊甸园区和一个幸存区。
好处:没有内存的碎片~
坏处:浪费了内存空间~ :多了一半空间永远是空to。假设对象100%存活(极端情况) 复制算法最佳使用。
标记清除算法
将幸存的对象标记,然后清除不被标记的对象。
优点:不会占用额外的空间。
缺点:将会进行两次扫描,占用时间长。
标记压缩算法
将幸存的对象标记,然后清除不被标记的对象。再将对象压缩在一个空间里面。
总结
三大gc算法比较(不含程序计数算法)
内存效率:复制算法>标记清除算法>标记清除压缩算法
内存整齐度:复制算法=标记清除压缩>标记清除
内存利用率:标记清除=标记压缩>复制算法
没有最好的算法,只有合适的算法--------GC分代收集算法
年轻代:
存活率低------复制算法
老年代:
存活率大-----标记清除几次+标记压缩