Java虚拟机

-
类加载器(Class Loader)
-
作用:加载class
-
分类:
-
应用程序类加载器(ApplicationClassLoader)
-
扩展类加载器(ExtensionClassLoader)
-
根类加载器(BootStrapClassLoader)
-
自定义类加载器(UserClassLoader)
-
-
-
运行时数据区(Runtime Data Area)
-
线程私有
-
Java栈(Stack):存储函数运行中的变量
-
本地方法栈(Native Method Stack)
-
程序计数器
-
-
全局共享
-
堆(Heap):存对象
-
方法区(Method Area)
-
栈和计数器中不存在垃圾,所以JVM调优99%是在调方法区和堆
-
下面以一个例子说明jvm实际内存运行过程:
类加载和双亲委派机制
-
类加载机制
-
JVM使用Java类的流程如下:
-
Java源文件----编译---->class文件
-
类加载器ClassLoader会读取这个.class文件,并将其转化为java.lang.Class的实例。有了该实例,JVM就可以使用他来创建对象,调用方法等操作了。
那么ClassLoader是以一种什么机制来加载Class的?这就要求我们要了解Class文件有哪些来源,针对这些ClassJDK如何分工,谁来加载这些Class。
-
-
Class文件的来源
-
Java内部自带的核心类,位于$JAVA_HOME/jre/lib,其中最著名的莫过于rt.jar
-
Java的扩展类,位于$JAVA_HOME/jre/lib/ext目录下
-
我们自己开发的类或项目开发用到的第三方jar包,位于我们项目的目录下,比如WEB-INF/lib目录
-
-
类加载器的分类
-
Java核心类,这些Java运行的基础类,由一个名为BootstrapClassLoader加载器负责加载。这个类加载器被称为“根加载器或引导加载器”。
注意:BootstrapClassLoader不继承ClassLoader,是由JVM内部实现。法力无边,所以你通过java程序访问不到,得到的是null。
-
Java扩展类,是由ExtClassLoader负责加载,被称为“扩展类加载器”。
-
项目中编写的类,是由AppClassLoader来负责加载,被称为“系统类加载器”。
-
-
-
双清委派机制
-
类加载器受到类加载的请求,将这个请求向上委托给父类加载去完成,一直向上委托,直到根加载器(寻根:App -> Exc -> Boot)
-
根加载器检查是否能够加载当前这个类,能加载就结束,使用当前类加载器,否则,抛出异常,通知子加载器进行加载,循环往复,直到找到能够加载此类的加载器;如果最终仍未找到,则报错ClassNotFound(回溯:App <- Exc <- Boot)
-
沙箱安全机制
组成沙箱的基本组件:
-
字节码校验器
-
类装载器(作用)
-
防止恶意代码干涉善意代码(双亲委派机制)
-
守护了被信任的类库边界
-
将代码归入保护域,确定了代码可以进行哪些操作
-
Native关键字
-
凡是带了Native关键字的,说明java的作用范围达不到了,回去调用底层c语言的库
-
会进入本地方法栈,调用本地方法接口 JNI
-
JNI作用:扩展java的使用,融合不同的语言为java所用
-
它在内存区域中专门开辟了一块标记区域:本地方法栈,登机native方法;在最终执行的时候,通过JNI加载本地方法库中的方法
PC寄存器
每个线程都有一个程序计数器,是线程私有的
方法区
-
方法区是被所有线程共享的,此区域属于共享区间
-
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法中,但是 实例变量存在堆内存中,和方法区无关
栈
-
栈内存,主管程序的运行,生命周期和线程同步,线程结束,栈内存也就释放了
-
对于线程来说,不存在垃圾回收问题
-
栈主要存储:8大基本数据类型 + 对象引用 + 实例方法
堆
-
Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的
-
类加载器读取了类文件后,一般会把什么东西放在堆中?类,方法,常量,变量,所有引用类型的真实对象
-
分区
-
新生区(伊甸区):类诞生和成长的地方,甚至死亡
-
伊甸园(E):所有对象都是在伊甸园中new出来的
-
幸存0区(S0)
-
幸存1区(S1)
-
-
年老区
-
永久区(元空间):常驻内存;用来存放jdk自身携带的Class对象。Interface元数据,存储的是java运行时的一些环境;这个区域不存在垃圾回收;关闭JVM虚拟机就会释放这个区域的内存
-
分析OOM的原因
-
先扩大运行内存,若扩大后运行成功,则说明内存不足,否则转第二步(-Xms 设置初始化内存分配大小;-Xmx 设置最大分配内存;-XX: +PrintGCDetails 打印gc信息)
-
使用辅助测试工具 Jprofiler
-
分析Dump内存,快速定位内存泄漏(-XX:+HeapDumpOnOutOfMemoryError)
-
获取堆中的数据
-
获取大的对象
-
GC
-
如何判断一个对象是否应该删除?
GCRoot不可删除(被栈,本地方法栈或者方法区直接或间接引用),其余可删除
-
GC算法
-
引用计数法
-
复制算法
-
流程:每次GC都会将Eden活的对象移到幸存区中:一旦Eden区被GC后,就会是空的;幸存区谁空谁是To;当一个对象经历了15次GC,都还没有死,就会移入年老区(-XX:MaxTenuringThreshold=9999 设定进入年老代的时间)
-
优缺点:优点是没有内存碎片;缺点是浪费了内存空间,多了一半空间永远是空To
-
最佳使用场景:对象存活度较低的时候(新生区)
-
-
标记清除算法
-
流程:对活着的对象进行标记;对没有标记的对象,进行清除;
-
优缺点:优点是不需要额外的空间;缺点是两次扫描,严重浪费时间,会产生内存碎片
-
最佳使用场景:对象存活度较高的时候(养老区)
-
-
标记压缩算法
-
流程:和标记清除相同,只是多了个压缩的过程:防止内存碎片产生,再次扫描,向一段移动存活的对象;多了一个移动成本
-
优缺点:和标记清除相同并且多了个移动成本
-
最佳使用场景:对象存活度较高的时候(养老区)
-
GC算法效率对比:
-
内存效率:复制算法 > 标记清除 > 标记压缩
-
内存整齐度:复制算法 = 标记压缩 > 标记清除
-
内存利用率:标记压缩算法 = 标记清除算法 > 复制算法
-

浙公网安备 33010602011771号