Java虚拟机(JVM,Java Virtual Machine)是一个抽象的计算机器,Java虚拟机是一个程序,对在它里面运行的编写的程序来说, 看起来像一个机器。这样,Java程序就会用相同的接口和库来编写。每一个针对特定操作系统的JVM实现,把Java程序指令翻译成运行在本地操作系统的指令和命令。这样,Java程序实现 了平台独立。
https://blog.csdn.net/qq_33916060/article/details/78309141
https://www.cnblogs.com/cornucopia2015/p/4811333.html
JVM和JMM的关系和区别:
https://blog.csdn.net/zhaomengszu/article/details/80270696
https://blog.csdn.net/sinat_33921105/article/details/82819435
jmm中的主内存、工作内存与jvm中的Java堆、栈、方法区等并不是同一个层次的内存划分,这两者基本上是没有关系的,如果两者一定要勉强对应起来,那从变量、主内存、工作内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分,而****工作内存则对应于虚拟机栈中的部分区域。从更低层次上说,主内存就直接对应于物理硬件的内存**,而为了获取更好的运行速度,虚拟机(甚至是硬件系统本身的优化措施)可能会让工作内存优先存储于寄存器和高速缓存中,因为程序运行时主要访问读写的是工作内存。**
垃圾收集器详解:
https://www.jb51.net/article/105581.htm
编译器详解:
https://www.cnblogs.com/royi123/p/3569511.html
栈帧详解:
http://www.imooc.com/article/270273?block_id=tuijian_wz
垃圾回收算法:
https://www.cnblogs.com/andy-zcx/p/5522836.html
**反射相关的参考我的另一篇博客:**https://blog.csdn.net/qinian8/article/details/100778343
参考文章:
https://www.jianshu.com/p/115899be002b
一、概述
程序计数器、虚拟机栈、本地方法栈。这几个区域完全不用管回收问题,因为方法结束或者线程结束的时候他们所占用的内存就自然跟着一起释放了,3个区域随线程而生,随线程而灭。所以我们只需要管堆和方法区。尤其是堆,因为一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,这部分内存的分配和垃圾回收都是动态的。
二、引用计数法(ReferenceCounting)
1、算法
给对象中添加一个引用计数器,每当有一个地方引用他时,计数器值就+1,;当引用失效时,计数器值就-1;任何时刻计数器为0的对象就是不可能在被使用。
2、图解
Paste_Image.png
3、优缺点
(1)、优点
判定效率很高
(2)、缺点
不会完全准确,因为如果出现两个对象相互引用的问题就不行了。如下代码所示:
/**
* testGC()方法执行后会不会被GC? 不会!!!!
*
* @author TongWei.Chen 2017-09-05 11:15:53
*/
public class ReferenceCountingGC {
public Object instance = null;
public static void testGC() {
//step 1
ReferenceCountingGC objA = new ReferenceCountingGC();
//step 2
ReferenceCountingGC objB = new ReferenceCountingGC();
//相互引用
//step 3
objA.instance = objB;
//step 4
objB.instance = objA;
//step 5
objA = null;
//step 6
objB = null;
//假设在这行发生CG,objA和objB是否能被回收? 不能!!!!
System.gc();
}
public static void main(String[] args) {
testGC();
}
}
(4)、分析上述代码
step1:objA的引用+1 =1
step2:objB的引用+1 =1
step3:objB的引用+1 =2
step4:objA的引用+1 =2
step5:objA的引用-1 =1
step6:objB的引用-1 =1
很明显,到最后两个实例都不再用了(都等于null了),但是GC却无法回收,因为引用数不是0,而是1,这就造成了内存泄漏。也很明显,现在虚拟机都不采用此方式。
三、可达性分析算法(Reachability Analysis)
1、算法
通过一系列的GC Roots的对象作为起始点,从这些根节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
2、图解
Paste_Image.png
说明:
(2.1)、红色代表不可达对象(可回收对象)
(2.2)、**千万注意!!!!!**上图并不是说方法区全可达,虚拟机栈部分可达,本地方法栈全部不可达,而只是为了说明这三个部分可以作为GC Roots!
3、可以作为GC Roots的对象包括以下几点
(3.1)、虚拟机栈(栈帧中的本地变量表)中引用的对象。
(3.2)、方法区中的类静态属性引用的对象或者常量引用的对象。
(3.3)、本地方法栈中JNI(就是native方法)引用的对象。
只有当oldspace快满的时候吗?
参考文章:
https://blog.csdn.net/skiof007/article/details/51839271
除直接调用System.gc**外,触发Full **GC执行的情况有如下四种。
1. 旧生代空间不足
旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:
java.lang.OutOfMemoryError: Java heap space
为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。
2. Permanet Generation空间满(永久代)(方法区/元空间)
PermanetGeneration中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:
java.lang.OutOfMemoryError: PermGen space
为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。
3. CMS GC时出现promotion failed和concurrent mode failure
对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。
promotionfailed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;concurrent mode failure是在执行CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。
应对措施为:增大survivorspace、旧生代空间或调低触发并发GC的比率,但在JDK 5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况,可通过设置-XX:CMSMaxAbortablePrecleanTime=5(单位为ms)来避免。
4. 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间
这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。
例如程序第一次触发MinorGC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。
当新生代采用PSGC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。
除了以上4种状况外,对于使用RMI来进行RPC或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。可通过在启动时通过- java-Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。
参考文章:https://bbs.csdn.net/topics/392176004
大对象直接进入老年代有几个比较坑爹的地方:
JDK版本 1.7.0_79
**新对象大于Eden区总大小的时候,会被直接扔到老年代,**还是要看Eden区的剩余大小?
试验:
-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:+UseParNewGC
如果一个类加载器收到了类加载的请求,首先不会自己去加载,而是把请求为派给自己的父类加载器去完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到顶层的启动类加载器中。只有当父类反馈自己无法完成这个加载请求时,子类加载器才会尝试自己完成。
双亲委派机制的工作流程:
- 当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。
每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.
当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。
https://www.cnblogs.com/aspirant/p/8663872.html
也包含CMS/G1 GC的区别
https://www.cnblogs.com/feiyudemeng/p/8276911.html
也叫GC优化
优化目标:
GC日志显示,高峰期CMS在重标记(Remark)阶段耗时1.39s。Remark阶段是Stop-The-World(以下简称为STW)的,即在执行垃圾回收时,Java应用程序中除了垃圾回收器线程之外其他所有线程都被挂起,意味着在此期间,用户正常工作的线程全部被暂停下来,这是低延时服务不能接受的。本次优化目标是降低Remark时间。