类的加载过程
多个java文件经过编译打包生成可运行的jar包,由java命令运行某个主类的main函数启动程序。
首先通过类加载器把主类加载到JVM,jar包中的类不是一次性加载完成的,使用的时候再加载。
类加载的流程
加载 > 验证 > 准备 > 解析 > 初始化 > 使用 > 卸载
加载:在硬盘上查找并通过IO读入字节码文件。使用到某个类的时候就会去加载。
验证:校验字节码文件的正确性。
准备:给类的静态变量分配内存,并赋予默认值。
解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如 main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成)。
初始化:对类的静态变量初始化为指定的值,执行静态代码块。
类加载器和双亲委派机制
类加载器:
启动类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等。
扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中 的JAR类包。
应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类。
自定义加载器:负责加载用户自定义路径下的类包
双亲委派机制
先找父亲加载,不行再由儿子加载。
堆
年轻代:Eden与Survivor区默认8:1:1
大多数情况下,对象在堆中分配内存,eden伊甸园区没有足够的空间进行分配时,虚拟机发起一次Minor GC。
老年代:存放老年对象。每次Minor GC之后都会计算来年代的空间,老年代空间不够用了会发生一次full GC。full GC之后空间依然不够用就OOM.
对象进入老年代
大对象直接进入老年代
JVM参数 XX:PretenureSizeThreshold 可以设置大对象的大小,如果对象超过设置大小 会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集 器下有效。
长期存活的对象进入老年代
每一个对象都有一个对象年龄计数器.对象在 Eden 出生,第一次经过一次Minor GC并且能被 Survivor 容纳,将移入Survivor区,对象年龄+1。在Survivor区中每经历过一次Minor GC,年龄+1.到达15岁(默认值)就进入老年代。
进入老年代岁数配置参数:-XX:MaxTenuringThreshold
动态判断
有n个对象(年龄1+年龄2+...+年龄N),当前放对象的Survivor区域里(其中一块区域,放对象的那块s区),一批对象的总 大小大于这块Survivor区域内存大小的50%,那么此时大于等于这批对象年龄最 大值的对象,就可以直接进入老年代了.
Minor gc后存活的对象Survivor区放不下
这种情况会把存活的对象部分挪到老年代,部分可能还会放在Survivor区
判断对象可以被回收
引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用 失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。
可达性分析算法
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点, 从这些节点开始向下搜索,找到的对象都标记为非垃圾对象,其余未标记的对象 都是垃圾对象 GC Roots根节点:线程栈的本地变量、静态变量、本地方法栈的变量等等
引用类型
强引用:普通对象的引用
软引用:
弱引用:
虚引用:
垃圾收集算法
标记清除:标记和清除,首先标记出所有需要回收的对象,在标记完成之后统一回收所有被标记的对象。会有空间问题,(标记清除之后会产生大量不连续的空间碎片)。
复制算法:将内存分为大小相同的两块,每次使用一块。当一块内存使用完后,将还存活的对象移到另一块去,然后把使用的空间一次清理掉。
标记整理:根据老年代的特点特出的一种标记算法,标记过程仍然与“标记-清除”算法一 样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一段移 动,然后直接清理掉端边界以外的内存。
分代收集:根据不同代的特性选用前面不同的收集算法。
逃逸分析
对象不一定分配在堆中。
JVM的运行模式有三种:
解释模式(Interpreted Mode):只使用解释器(-Xint 强制JVM使用解释模式),执行一行JVM字节码就编译一行为机器码。
编译模式(Compiled Mode):只使用编译器(-Xcomp JVM使用编译模式),先将所有JVM字节码一次编译为机器码,然 后一次性执行所有机器码。
混合模式(Mixed Mode):依然使用解释模式执行代码,但是对于一些 "热点" 代码采用编译模式执行,JVM一般采用混合模 式执行代码 解释模式启动快,对于只需要执行部分代码,并且大多数代码只会执行一次的情况比较适合;编译模式启动慢,但是后期执行速度快,而 且比较占用内存,因为机器码的数量至少是JVM字节码的十倍以上,这种模式适合代码可能会被反复执行的场景;混合模式是JVM默认采 用的执行代码方式,一开始还是解释执行,但是对于少部分 “热点 ”代码会采用编译模式执行,这些热点代码对应的机器码会被缓存起 来,下次再执行无需再编译,这就是我们常见的JIT(Just In Time Compiler)即时编译技术。
在即时编译过程中JVM可能会对我们的代码最一些优化,比如对象逃逸分析等
对象逃逸分析:就是分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中。这个时候对象可以被分配在栈中,当方法结束,对象就被回收了。
finalize()方法
最终判定对象是否存活,即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们 暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。 标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链。
1. 第一次标记并进行一次筛选。 筛选的条件是此对象是否有必要执行finalize()方法。 当对象没有覆盖finalize方法,对象将直接被回收。 2. 第二次标记 如果这个对象覆盖了finalize方法,finalize方法是对象脱逃死亡命运的最后一次 机会,如果对象要在finalize()中成功拯救自己,只要重新与引用链上的任何的一 个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第 二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本 上它就真的被回收了。
JVM调优
参数
- -Xms:是指设定程序启动时占用内存大小。
- -Xmx:是指设定程序运行期间最大可占用的内存大小。
- -Xmn:新生代大小
- -Xss:一个线程栈能分配栈帧的大小。
- -XX:+PrintGC :打印gc
- -XX:PrintHeapAtGC :打印GC前后的详细堆栈信息
- -XX:+PrintGCDetails
- -XX:+UseSerialGC :设置串行收集器
- -XX:+UseParallelGC :设置并行收集器
- -XX:+UseParalledlOldGC :设置并行年老代收集器
- -XX:+UseConcMarkSweepGC :设置并发收集器
- -XX:+HeapDumpOnOutOfMemoryError :当oom的时候到处堆信息
- -XX:HeapDumpPath=./ (路径)
工具
Jmap 查看
jvisualvm
Jstack
Jinfo
Jstat
jps
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。