初探JVM
【面试常见问题】
- 请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?
- 什么是OOM,什么是栈溢出StackOverFlowError?怎么分析
- JVM的常用调优参数有哪些
- 内存快照如何抓取,怎么分析Dump问件?
- 谈谈JVM中,类加载器你的认识
文章目录
- JVM的位置
处于操作系统和java程序编译过的java字节码之间
- JVM的体系结构(在jdk1.8之后的方法区在堆的元空间里了)
类加载器
作用:加载class文件,用于实现类的加载动作。
public class Car {
public int age;
public static void main(String[] args) {
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
System.out.println(car1.hashCode());
System.out.println(car2.hashCode());
System.out.println(car3.hashCode());
//通过对象获取类
Class<? extends Car> aClass1 = car1.getClass();
System.out.println(aClass1);
ClassLoader classLoader1 = aClass1.getClassLoader();
System.out.println(classLoader1);//AppClsssLoader 应用程序加载器
System.out.println(classLoader1.getParent()); //ExtClassLoader 扩展类加载器
System.out.println(classLoader1.getParent().getParent());//null 为空的可能原因:1.不存在 2.java程序获取不到,可能因为底层是C语言写的,所以访问不到
}
}
- 虚拟机自带的加载器
- 启动类(根)加载器
- 扩展类加载器
- 应用程序加载器
双亲委派机制
双亲委派机制:安全
通过getClassLoader执行顺序为 App—>EXC—>Boot(最终执行)
- 类加载器收到类加载的请求
- 将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器(向上委托,向下加载)
- 启动类加载器检查是否能够加载当前的这个类,就能加载就结束,否则抛出异常,通知子加载器进行加载
1、翻译的问题,parents翻译为父母,即双亲。2. AppClassLoader 向上委托了两次,即“双”,“亲”代表亲人的意思对双亲的理解。3、或者直接理解成父委派模型(Parents Delegation Model)
沙箱安全机制
程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它可以被看做是当前线程所执行的字节码的行号指示器,它的工作就是通过改变计数器的值来选取下一条需要执行的字节码指令,它控制着程序的分支、循环、跳转、异常处理、线程恢复等基础功能。若缺少程序计数器,java程序中的流程控制将无法得到正确的控制,多线程也无法正确的轮换。【注】:当程序执行的是java方法时,这个计数器指向的是该方法的字节码地址,当执行的是本地方法时,则计数器的值为空(undefined)。次内存区域时唯一一个在《java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。
方法区
Method Area方法区
方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也再此定义,简单说,搜友定义的信息都保存在该区域,此区域属于共享区间。
静态变量,常量,类信息(构造方法,接口定义)、运行时的常量池存放在方法区,但是实例变量存在堆内存中,和方法区无关
static final Class 常量池
java虚拟机栈
插一嘴自学编程的一些心得,一个好的程序员是需要对思想、逻辑、创新等方面的塑造
程序 = 数据结构 + 算法 : 持续学习
程序 = 框架 + 业务逻辑 : 吃饭~
栈:数据结构
栈:先进先出、后进后出
队列:先进先出(FIFO:frist in frist out)
为什么main()先执行,最后结束?看完后面你会得到答案!
java虚拟机栈:栈内存私有,主管程序的运行,生命周期和线程同步,线程结束,栈内存也就是释放,对于栈来说,不存在垃圾回收问题。在每个方法被执行的时候,虚拟机都会同步创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法出口等信息,每个方法被调用到执行都是栈帧在java虚拟机栈中栈入栈出的过程。在《java虚拟机规范》中,对于这个内存区域规定了两类异常情况:1、当线程请求的栈深度大于虚拟机所允许的深度,则抛出StackOverflowError异常;如果是栈容量可扩展的虚拟机中,当栈满时,抛出的是OutOfMemoryError异常。
栈中存储内容:8大基本类型 + 对象引用 + 实例的方法 byte short int long float double boolean char
因为虚拟机栈描述的是java方法执行的线程内存模型,所以上述所说的存储内容是特指局部变量,是在方法里的变量信息。
栈运行原理:栈帧(一个栈帧可以看作是一个类中的方法)
栈满了:StackOverflowError
栈 + 堆 + 方法区:交互关系
Native本地方法栈
所发挥的作用和java虚拟机发挥的作用是非常相似的,其区别就是执行的是本地(Native)方法服务。有特殊的java虚拟机(譬如Hot-Spot虚拟机)就直接把本地方法栈和java虚拟机栈合二为一了。
三种JVM
- Sun公司的 HotSpot
- BEA
JRockit
- IBM
J9VM
我们学习都是:HotSpot
堆(重点)
Heap,一个JVM只有一个堆内存,堆内存的大小可以调节的。
类加载器读取类文件后,一般会把什么东西放到堆中? 类,类中的方法,常量,变量,保存我们所有引用类型的真实对象;
堆内存中还要细分三个区域:
-
新生区(伊甸园区)
-
养老区 old
-
永久区(jdk8之后就是元空间了)
GC垃圾回收,主要是在伊甸园区和养老区~
假设内存满了,OOM,堆内存不够! java.lang.OutOfMemoryError:java heap space
在JDK8以后,永久区改了名字(元空间)
新生区
- 类:诞生和成长的地方,甚至是死亡
- 伊甸院,所有的对象都是在伊甸元区 new 出来
- 幸存区(0,1)
真理:经过研究,99%的对象都是临时对象
养老区
永久区
这个区域常驻内存的,用来存放JDK自身携带的Class对象,Interface元数据,存储的是java运行时的一些环境或者类信息,这个区域不存在垃圾回收!关闭VM虚拟就会释放这个区域的内存~
一个启动类加载,加载大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的放射类。不断的被加载。直到内存满,就会出现OOM;
- jdk1.6之前:永生代,常量池是在方法区;
- jdk1.7 :永久代,但是慢慢退化了,失去永久代,常量池在堆中
- jdk1.8之后:无永久代,常量池在元空间
元空间:逻辑上存在,物理上不存在
//电脑默认情况下:分配的堆总内存是电脑内存的1/4,初始化内存为1/64
//OOM解决方法:
//1.尝试扩大堆内存看结果
//2.分析内存,看一下哪个地方出现了代码问题(使用专业工具)
扩大内存解决OOM:
排除OOM故障:
- 能够看到代码低几行出错,内存快照分析工具,MAT,jprofiler
- Debug,一行行代码分析
MAT,jprofiler作用:
- 分析Dump内存文件,快速定位内存泄漏;
- 获得堆中的数据
- 获取大的数据
- …
-Xms 设置初始化内存分配大小 1/64
-Xmx设置最大分配内存 默认 1/4
-XX:+PrintGCDetails 打印GC垃圾回收信息
-XX:+HeapDumpOnOutOfMemoryError
-Xms1m -Xmx1m -XX:+HeapDumpOnOutOfMemoryError
可以在src同根目录下找到Dump出来的信息
GC垃圾回收
垃圾回收是回收堆里面的东西
- 新生代
- 幸存区(from ,to)GC刷新15次,存货下来的到老年区
- 老年区
GC两种类:轻GC(普通GC),重GC
GC题目:
- JVM的内存模型和分区,详细到每个区放什么
- 堆里面的分区有哪些?Eden,from,to,老年区,说说他们的特点
- GC的算法有哪些?标记清除法,标记整理,复制算法,引用计数法,怎么用?
- 轻GC和重GC分别在什么时候方法?
引用计数法:
复制算法:
- 好处:没有内存的碎片
- 坏处:浪费了内存空间~:多了一半空间永远是空to,假设对象100%存活(极端情况)
复制算法最佳使用场景:对象存货度较低的时候;新生区~
标记清除法:
- 缺点:两次扫描,严重浪费时间,会产生内存碎片
- 优点:不需要额外的空间
标记压缩:
再优化
总结:
内存效率:复制算法 > 标记清楚算法 > 标记压缩清除算法(时间复杂度)
内存整齐度:复制算法 = 标记压缩清除算法 > 标记清楚算法
内存利用率:标记压缩清除算法 = 标记清楚算法 > 复制算法
没有最好的算法,只有最合适的算法----->GC:分代收集算法
年轻代:
- 存活率滴(复制算法)
老年代:
- 存活率高(标记清楚压缩算法)
JMM学习方式
-
什么是JMM?
JMM:Java Memory Model的缩写
-
他是干嘛的?:官方,其他人的博客,对应的视频
作用:缓存一致性协议,用于定义数据读写的规则(遵守,找到这个规则)
JMM定义了线程工作内存和主内存之间的抽象关系,线程之间的共享变量存储再主内存(main memory)中,每个线程都有一个私有的本地内存(local memory)