一 JVM结构图
二 类加载器
1 JVM运行过程
1.java文件编译成.class文件(也称字节码文件)
2.由类加载器对字节码文件进行:效验-准备-解析-初始化的过程,加载到JVM内存(其中class文件是运行期动态按需加载的,不是一下全部加载进内存)
3.不同操作系统的JVM版本,将字节码文件翻译成当前操作系统指令.
2 类加载器分类
- 启动类加载器(BootStrapClassLoader)
底层由C/C++编写,最顶层加载器负责JDK的核心类库(rt.jar、resources.jar、charsets.jar)比如String,int等类. - 扩展类加载器(ExtClassLoader)
主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的所有jar - 应用类加载器(AppClassLoader)
classpath底下的类,一般指的就是我们平时编写的类 - 自定义加载器
3 类加载机制
- 全盘负责委托机制
所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入 - 双亲委派机制
是指子类加载器如果没有加载过该目标类,就先委托父类加载器加载该目标类,只有在父类加载器找不到字节码文件的情况下才从自己的类路径中查找并装载目标类.比如我们自定义一个类,本身属于应用类加载器,但是要先传递给扩展类加载器,扩展类加载器再传递给启动类加载器,如果上面两个都没有此类,才由应用类加载器加载. - 缓存机制
缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。 - 双亲委派机制的优势
沙箱安全机制:自己写的String.class类不会被加载,这样便可以防止核心API库被随意篡改
避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再 加载一次
三 JVM性能调优指令
- jps
查看java运行程序pid - jinfo
查看java程序的扩展参数# jinfo -flags pid //输出JVM全部参数,如堆 年轻代 老年代的参数 # jinfo -sysprops pid // 输出当前 jvm 进行的全部的系统属性,如jdk版本
- jstat
统计堆各内存使用量和类的使用量,性能分析# jstat -class pid //类的使用量 # jstat -gc pid // 垃圾回收统计 # jstat gccapacity pid //内存统计
- jmap
统计内存信息jmap -heap pid //打印heap的概要信息,GC使用的算法,heap(堆)的配置及JVM堆内存的使用情况 jmap -histo pid > ./log.text //实例个数及占用内存大小 jmap -dump,format=b,file=myjmapfile.txt pid // 使用hprof二进制形式,输出jvm的heap内容到文件
- jstack
查看线程,用来分析线程死锁等问题 - jVisualvm工具监控
三 垃圾回收
1、 什么情况下触发gc
新的对象首先进入eden区,当eden区内存满时,将进入survior区,对象如果一直不被回收,就会在survior区中的from和to区循环,循环到15次的时候,将进入老年代.期间如果survior区内存不够的时候,对象会直接进入老年代.所以要尽量避免大的对象出现.
minor GC:每eden区内存满一次,就会触发minor GC,minor GC回收eden和survior两个区无效的对象.
major GC:老年代内存慢的时候触发.
full GC: 老年代 元空间内存不足的时候会触发full GC, full GC会回收新生代-老年代-元空间三个.因为元空间一般不会出现内存不足,所以full GC有时候和major GC一个概念.
2 、 如何标记哪些对象为可回收
- 引用计数器法
有引用的时候计数器+1,引用完-1,计数器为0标记此对象可回收.弊端是互相引用的对象会一直留在堆内存中. - 可达性分析算法
以GC Roots为根节点向下搜索,如果一个对象和GC Roots没有任何关联,此时不会立即标记为可回收,可以通过重写finalize()标记此对象为可用状态,如数据库连接池.
GC Roots根节点:类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等等 - 判断一个无用的常量:没有任何对象引用.如 “abc” 没有任何String对象引用
- 判断一个无用的类
类的实例对象都回收, 没有对应的java.lang.class引用,没有反射,但即使标记的无用的类也不见得会回收,因为常量和类都存在元空间,元空间一般不会溢出.
3 、垃圾回收算法
- 概述
标记完可回收对象后,怎么整理内存空间最合适呢,此时就用到了垃圾回收算法 - 标记清楚算法
最原始的算法,直接将可回收的内存回收,弊端是会出现大量的不连续的碎片. - 复制算法
复制出一块大小相同的内存,将可用的复制到新的内存中,复制完将之前的内存全部变为可用的内存.
- 标记整理算法
很简单,就是将可用的内存空间向前移动,以达到可用内存为连续的内存空间. - 分代收集算法
新生代采用复制算法,老年代采用标记整理算法.原因是新生代可回收的内存多,可用内存少,复制起来快. 老年代可用内存多,移动起来快.
4 垃圾收集器
- 概述
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。