前端编译(.java --> .class)字节码 不等于 机器码,需要jvm将字节码加载到内存中,需要通过执行引擎将字节码 解释/编译成机器码 后端编译(.class --> 机器码)。
执行引擎机制:
逐行解释执行效率低
JVM会针对使用效率较高的热点代码进行编译,并缓存起来,执行效率提高
但是编译是需要消耗时间的。
所以jvm刚刚启动后,可以先通过解释器,解释执行代码
之后在使用编译器编译执行,两种结合在一起
垃圾收集机制并不是java语言创的,但是又是java的招牌,java可以自动垃圾回收
回收哪些区域:频繁回收堆内存,较少回收方法区,栈(溢出),本地方法栈(溢出),程序计数器没有垃圾回收。
while (true){
new Random().nextInt();
}
垃圾是指在运行程序中没有任何引用指向的对象,这个对象就是需要被回收的垃圾。
(1)垃圾如果不及时清理,越积越多,可能会导致内存溢出。
(2)垃圾多了,内存碎片较多,例如数组,需要连续空间
(3)随着应用程序应付的业务越来越大,没有GC就不能保证应用程序的正常进行。
早期是手动回收不被使用的对象,例如C++,java语言是自动垃圾收集的。
自动内存管理:无需开发人员手动参与内存的分配与回收,这样降低内存泄漏和内存溢出的风险。以更专心地专注于业务开发
自动收集的担忧:自动回收方便了程序员的开发,但是降低处理内存问题的能力。
自动虽好,但是还是应该了解并掌握一些相关内存管理知识。
从次数上讲:
频繁收集 Young 区
较少收集 Old 区
基本不收集元空间(方法区
溢出:内存不够用了
泄漏:有些对象已经在程序不被使用了,但是垃圾回收机制并不能判定其为垃圾对象,不能将其回收掉,这样的对象越积越多,长久也会导致内存不够用。
eg:
与数据库连接完之后,需要关闭连接通道,但是没有关闭。
io读写完成后没有关闭
主要是来判定哪些对象已经不再被使用,标记为垃圾对象,
判定对象为垃圾的标准:不被任何引用所指向的对象。Object obj =new Object();
如果有一个引用指向此对象,那么计数器加1,如果没有引用指向,计数器为0,此时就判定位垃圾。
优点:方便使用,设计简洁
缺点:增加了计数器的存储空间,计数需要消耗时间。
Object obj = new Object();
obj =null;
解决:循环引用问题 ,设计简单,运行高效,防止内存泄漏
思路:
从一些活跃引用(GCRoots根)开始,如果对象被根直接或间接引用,那么此对象不是垃圾,否则被标记为垃圾对象。
虚拟机栈中引用的对象(方法中引用的对象)
本地方法栈中引用的对象
静态变量所引用的对象
常量引用指向的对象
被synchronized当做锁的对象
java虚拟机内部的引用
java运行对象在销毁前去调用finalize(),去处理一些逻辑,一般不用(不建议用).不要显示的去调用finalize()方法,在里面写代码一定要慎重!!!
垃圾:不是立刻被回收,只是被标记,有了finalize()方法/机制的存在,我们的对象有可能起死回生。
对象状态:
可触及的:从根节点开始,可以到达这个对象。(没有被标记为垃圾)
可复活的:对象的所有引用都被释放,但是对象有可能在finalize()中复活。确定为垃圾了,但没有调用finalize()方法。
不可触及的:对象的finalize()被调用,并且没有复活,那么就会进入不可触及状态。不可触及的对象不可能被复活,因为finalize()只会内调用一次。
package com.ffyc.database.jvm.gc;
public class CanReliveObj {
public static CanReliveObj obj;//类变量,属于 GC Root
//此方法只能被调用一次
@Override
protected void finalize() throws Throwable {
//super.finalize();
System.out.println("调用当前类重写的finalize()方法");
obj = this;//当前待回收的对象在finalize()方法中与引用链上的一个对象obj建立了联系
}
public static void main(String[] args) {
try {
obj = new CanReliveObj();
// 对象第一次成功拯救自己
obj = null;
System.gc();//调用垃圾回收器,触发FULL GC 也不是调用后立刻就回收的,因为线程的执行权在操作系统
System.out.println("第1次 gc");
// 因为Finalizer线程优先级很低,暂停2秒,以等待它
Thread.sleep(2000);
if (obj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
System.out.println("第2次 gc");
// 下面这段代码与上面的完全相同,但是这次自救却失败了
obj = null;
System.gc();
// 因为Finalizer线程优先级很低,暂停2秒,以等待它
Thread.sleep(2000);
if (obj == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
优点:简单、容易理解
缺点:效率低,会产生STW(在回收时,停止整个应用程序),会产生内存碎片。
将内存分为大小相等的两块,每次只使用其中的一块儿区域即可。当回收时,将不是垃圾的对象,复制到另一块内存中,排放整齐。然后将原来的内存块清空。减少内存碎片。
一般在新生代中的幸存者0和幸存者1这两个区域使用复制算法。
标记-清除是不移动对象,不会把垃圾对象清除掉(维护在一个空闲列表中)
标记-压缩是要移动对象的,要清除掉垃圾对象。
由于对象的生命周期长短不同,将不同的对象存储在不同的区域;针对不同的区域进行分区收集,提高收集效率。
调用System.gc()方法,会触发Full GC(整堆收集),但是不一定调用后会立刻生效,因为垃圾回收是自动的,一般情况下,不要在项目中显示的去调用。
stop the world —> STW 在垃圾回收时,会导致整个应用程序停止。
在标记垃圾对象时,需要以某个时间节点上内存中的情况进行分析(拍照 快照),因为不进行停顿的话,内存中的对象不停的变化,导致分析结果不准确。停顿是不可避免的,优秀的垃圾回收器尽可能减少停顿的时间。
Object obj = new Object();
就是将对象分等级:强引用(有引用指向的对象)、软引用 、 弱引用 、虚引用(这三个都是垃圾了) 【在 java.lang.ref包中】
比较底层,了解垃圾回收器的一些种类及实现。
垃圾回收器(具体实现垃圾回收的收集器名称)
按线程数分,可以分为串行垃圾回收器和并行垃圾回收器。
按照工作模式分,可以分为并发式垃圾回收器和独占式垃圾回收器。
按工作的内存区间分,又可分为年轻代垃圾回收器和老年代垃圾回收器。
吞吐量:运行用户代码的时间占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间)
垃圾收集开销:垃圾收集所有时间与总运行时间的比例。
暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间。
收集频率:相对于应用程序的执行,收集操作发生的频率。
内存占用:java堆区所占的内存大小。
快速:一个对象从诞生到被回收所经历的时间。