垃圾收集GC(Garbage Collection)是Java语言的核心技术之一, 在Java中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM来处理。对于Java对象来讲,如果说这个对象没有被其他对象所引用该对象就是无用的,此对象就被称为垃圾,其占用的内存也就要被销毁。
当一个对象的引用(地址值)没有变量去记录的时候/没有被其他对象引用, 该对象就会成为垃圾对象, 并在垃圾回收器空闲的时候对其进行清扫.
public class Jvm_Gc {
public static void main(String[] args) {
//当一个对象的引用(地址)没有变量被记录的时候,该对象 就会成为垃圾对象, 并在垃圾回收器空闲的时候对其进行清扫
MyObject obj = new MyObject();
obj = null;//对象被赋空值, 没有记录引用/地址, 或者没有对象引用.这就是垃圾对象.
//实例2
new MyObject().show();
}
}
class MyObject{
public void show(){
System.out.println("我是方法show");
}
}
Java中标记垃圾的算法主要有两种, 引用计数法和可达性分析算法。
public class MyObject {
public MyObject childNode;
}
public class ReferenceCounterProblem {
public static void main(String[] args) {
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
object1.childNode = object2;
object2.childNode = object1;
}
}
从上述代码中我们可以看出,object1和object2并没有任何价值,但是他们循环引用,造成内存泄露。
可以重写Object类中的finallize方法,这个方法在垃圾执行的时候,被回收器自动调用执行, 日常编码不使用. 这里我们吧垃圾对象设多一点才会使jvm自动开始调用finalize处理垃圾,我们才能看到控制台的输出.
public class Jvm_Gc2 {
public static void main(String[] args) {
for (int i = 0;i <= 1000000; i++){
new MyObject2();
}
}
}
class MyObject2 extends Object{
@Override
public void finalize(){
System.out.println("这么多垃圾,我来处理一下!");
}
}
直接通知垃圾回收器来处理垃圾,调用Gc()方法.
public class Jvm_Gc2 {
public static void main(String[] args) {
for (int i = 0;i <= 1000000; i++){
new MyObject2();
System.gc();//直接通知GC
}
}
}
class MyObject2 extends Object{
@Override
public void finalize(){
System.out.println("我又来处理垃圾啦!");
}
}
在Java中存在着**四种垃圾回收算法,标记清除算法、复制算法、标记整理算法以及分代回收算法**。我们接下来会分别介绍他们。
由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC: 1.年老代(Tenured)被写满 2.持久代(Perm)被写满 3.System.gc()被显示调用 4.上一次GC之后Heap的各域分配策略动态变化
Static Vector v = new Vector();
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}
在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 o 。在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 o 引用置空。问题是当 o 引用被置空后,如果发生 GC,我们创建的 Object 对象是否能够被 GC 回收呢?答案是否定的。因为, GC 在跟踪代码栈中的引用时,会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管o 引用已经被置空,但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。如果在此循环之后, Object 对象对程序已经没有任何作用,那么我们就认为此 Java 程序发生了内存泄漏。
本文由 Alicyu 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
原文链接:https://www.alicyu.com/archives/jvm-gc
最后更新:2019-10-18 15:09:08
Update your browser to view this website correctly. Update my browser now