Java GC不收集一个;“僵尸”;第二次反对

Java GC不收集一个;“僵尸”;第二次反对,java,memory-management,garbage-collection,jvm,Java,Memory Management,Garbage Collection,Jvm,我试图创建一种机制,将对象缓存到内存中,以备将来使用,即使这些对象脱离上下文。将有一个并行的确定性过程,它将(通过一个唯一的ID)指示是否应该再次检索缓存的对象,或者它是否应该完全死亡。下面是一个最简单的示例,其中包含调试信息,使事情变得更简单: package com.panayotis.resurrect; import java.util.Map; import java.util.HashMap; public class ZObject { private static

我试图创建一种机制,将对象缓存到内存中,以备将来使用,即使这些对象脱离上下文。将有一个并行的确定性过程,它将(通过一个唯一的ID)指示是否应该再次检索缓存的对象,或者它是否应该完全死亡。下面是一个最简单的示例,其中包含调试信息,使事情变得更简单:

package com.panayotis.resurrect;

import java.util.Map;
import java.util.HashMap;

public class ZObject {

    private static int IDGEN = 1;

    protected int id;
    private boolean isKilled = false;

    public static final Map<Integer, ZObject> zombies = new HashMap<>();

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++)
            System.out.println("* INIT: " + new ZObject().toString());
        gc();
        sleep(1000);

        if (!zombies.isEmpty())
            ZObject.revive(2);

        gc();
        sleep(1000);

        if (!zombies.isEmpty())
            ZObject.kill(1);

        gc();
        sleep(1000);
        gc();
        sleep(1000);
        gc();
        sleep(1000);
        gc();
        sleep(1000);
    }

    public ZObject() {
        this.id = IDGEN++;
    }

    protected final void finalize() throws Throwable {
        String debug = "" + zombies.size();
        String name = toString();
        String style;
        if (!isKilled) {
            style = "* Zombie";
            zombies.put(id, this);
        } else {
            style = "*** FINAL ***";
            zombies.remove(id);
            super.finalize();
        }
        dumpZombies(style + " " + debug, name);
    }

    public String toString() {
        return (isKilled ? "killed" : zombies.containsKey(id) ? "zombie" : "alive ") + " " + id;
    }

    public static ZObject revive(int peer) {
        ZObject obj = zombies.remove(peer);
        if (obj != null) {
            System.out.println("* Revive      " + obj.toString());
            obj.isKilled = false;
        } else
            System.out.println("* Not found as zombie " + peer);
        return obj;
    }

    public static void kill(int peer) {
        int size = zombies.size();
        ZObject obj = zombies.get(peer);
        String name = obj == null ? peer + " TERMINATED " : obj.toString();
        zombies.remove(peer);
        dumpZombies("*   Kill " + size, name);
        if (obj != null)
            obj.isKilled = true;
    }

    private static void dumpZombies(String baseMsg, String name) {
        System.out.println(baseMsg + "->" + zombies.size() + " " + name);
        for (Integer key : zombies.keySet())
            System.out.println("*             " + zombies.get(key).toString());
    }

    public static void gc() {
        System.out.println("* Trigger GC");
        for (int i = 0; i < 50; i++)
            System.gc();
    }

    public static void sleep(int howlong) {
        try {
            Thread.sleep(howlong);
        } catch (InterruptedException ex) {
        }
    }
}
package com.panayotis.resurrect;
导入java.util.Map;
导入java.util.HashMap;
公共类对象{
专用静态int IDGEN=1;
受保护的int-id;
私有布尔值iskill=false;
public static final-Map zombies=new HashMap();
公共静态void main(字符串[]args){
对于(int i=0;i<5;i++)
System.out.println(“*INIT:+new ZObject().toString());
gc();
睡眠(1000);
如果(!zombies.isEmpty())
苏醒(2);
gc();
睡眠(1000);
如果(!zombies.isEmpty())
ZObject.kill(1);
gc();
睡眠(1000);
gc();
睡眠(1000);
gc();
睡眠(1000);
gc();
睡眠(1000);
}
公共对象(){
this.id=IDGEN++;
}
受保护的最终void finalize()可丢弃{
String debug=“”+zombies.size();
字符串名称=toString();
弦乐风格;
如果(!isKilled){
style=“*僵尸”;
僵尸。放置(id,this);
}否则{
style=“***最终***”;
僵尸。移除(id);
super.finalize();
}
转储僵尸(样式+“”+调试,名称);
}
公共字符串toString(){
return(isKilled?“killed”:zombies.containsKey(id)?“zombie”:“live”)+“”+id;
}
公共静态ZObject恢复(int对等){
ZObject obj=僵尸。移除(对等);
如果(obj!=null){
System.out.println(“*reserve”+obj.toString());
obj.iskill=false;
}否则
System.out.println(“*未作为僵尸找到”+对等);
返回obj;
}
公共静态无效终止(int对等){
int size=zombies.size();
ZObject obj=zombies.get(对等);
字符串名称=obj==null?对等方+“终止”:obj.toString();
僵尸。移除(对等);
转储僵尸(“*杀死”+大小,名称);
如果(obj!=null)
obj.iskill=true;
}
私有静态无效转储僵尸(字符串baseMsg,字符串名称){
System.out.println(baseMsg+“->”+zombies.size()+”+name);
for(整数键:zombies.keySet())
System.out.println(“*”+zombies.get(key.toString());
}
公共静态void gc(){
System.out.println(“*触发器GC”);
对于(int i=0;i<50;i++)
gc();
}
公共静态无效睡眠(int-howlong){
试一试{
睡眠(呼噜声);
}捕获(中断异常例外){
}
}
}
这段代码将创建5个对象,复活第一个对象,然后杀死第一个对象。我期待着

  • 在第一次复活之后,由于对象还没有更多的引用,通过finalize(它没有)重新进入僵尸状态

  • 再次杀死一个对象后,通过finalize方法将其从内存中完全删除

换句话说,finalize似乎只调用一次。我已经检查过这不是HashMap对象与以下代码的副产品:

package com.panayotis.resurrect;

import java.util.HashMap;

public class TestMap {

    private static final HashMap<Integer, TestMap> map = new HashMap<>();

    private static int IDGEN = 1;
    private final int id;

    public static void main(String[] args) {
        map.put(1, new TestMap(1));
        map.put(2, new TestMap(2));
        map.put(3, new TestMap(3));
        map.remove(1);
        System.out.println("Size: " + map.size());
        for (int i = 0; i < 50; i++)
            System.gc();
    }

    public TestMap(int id) {
        this.id = id;
    }

    protected void finalize() throws Throwable {
        System.out.println("Finalize " + id);
        super.finalize();
    }
}
package com.panayotis.resurrect;
导入java.util.HashMap;
公共类测试图{
private static final HashMap map=new HashMap();
专用静态int IDGEN=1;
私有最终int id;
公共静态void main(字符串[]args){
map.put(1,新TestMap(1));
map.put(2,新TestMap(2));
map.put(3,新TestMap(3));
地图。删除(1);
System.out.println(“Size:+map.Size());
对于(int i=0;i<50;i++)
gc();
}
公共测试映射(int-id){
this.id=id;
}
受保护的void finalize()抛出可丢弃的{
系统输出打印项次(“最终确定”+id);
super.finalize();
}
}
那么,为什么会有这种行为呢?我正在使用Java1.8


编辑由于这是不可能直接实现的,您知道我如何实现它吗?

您不应该实现finalize方法,因为GC只会为每个实例调用它一次

因此,如果GC将找到要删除的对象,它将调用finalize。然后,它将再次检查可能的新参考。它可能会找到一个并将对象保留在内存中


在下一次运行时,同样的对象将没有任何引用。GC只会杀死它,它不会再次调用finalize。

您不应该实现finalize方法,因为GC只会为每个实例调用它一次

因此,如果GC将找到要删除的对象,它将调用finalize。然后,它将再次检查可能的新参考。它可能会找到一个并将对象保留在内存中


在下一次运行时,同样的对象将没有任何引用。GC只会杀死它,它不会再次调用finalize。

这正是指定的行为:

在为对象调用
finalize
方法后,在Java虚拟机再次确定任何尚未终止的线程都无法再访问该对象之前,不会采取进一步的操作,包括准备终止的其他对象或类的可能操作,此时对象可能会被丢弃

Java虚拟机对任何给定对象都不会多次调用
finalize
方法

您似乎对
finalize()
方法的作用有错误的理解。此方法不会释放对象的内存,而是声明自定义非
// front-end class
public class Resource {
    final ActualResource actual;

    Resource(ActualResource actual) {
        this.actual = actual;
    }
    public int getId() {
        return actual.getId();
    }
    public String toString() {
        return actual.toString();
    }
}
class ActualResource {
    int id;

    ActualResource(int id) {
        this.id = id;
    }

    int getId() {
        return id;
    }

    @Override
    public String toString() {
        return "ActualResource[id="+id+']';
    }
}
public class ResourceManager {
    static final ReferenceQueue<Resource> QUEUE = new ReferenceQueue<>();
    static final List<ActualResource> FREE = new ArrayList<>();
    static final Map<WeakReference<?>,ActualResource> USED = new HashMap<>();
    static int NEXT_ID;

    public static synchronized Resource getResource() {
        for(;;) {
            Reference<?> t = QUEUE.poll();
            if(t==null) break;
            ActualResource r = USED.remove(t);
            if(r!=null) FREE.add(r);
        }
        ActualResource r;
        if(FREE.isEmpty()) {
            System.out.println("allocating new resource");
            r = new ActualResource(NEXT_ID++);
        }
        else {
            System.out.println("reusing resource");
            r = FREE.remove(FREE.size()-1);
        }
        Resource frontEnd = new Resource(r);
        USED.put(new WeakReference<>(frontEnd, QUEUE), r);
        return frontEnd;
    }
    /**
     * Allow the underlying actual resource to get garbage collected with r.
     */
    public static synchronized void stopReusing(Resource r) {
        USED.values().remove(r.actual);
    }
    public static synchronized void clearCache() {
        FREE.clear();
        USED.clear();
    }
}
static final ResourceManager manager = new ResourceManager();
public static void main(String[] args) {
    Resource r1 = manager.getResource();
    Resource r2 = manager.getResource();
    System.out.println("r1 = "+r1+", r2 = "+r2);
    r1 = null;
    forceGC();

    r1 = manager.getResource();
    System.out.println("r1 = "+r1);
    r1 = null;
    forceGC();

    r1 = manager.getResource();
    System.out.println("r1 = "+r1);

    manager.stopReusing(r1);

    r1 = null;
    forceGC();

    r1 = manager.getResource();
    System.out.println("r1 = "+r1);
}
private static void forceGC() {
    for(int i = 0; i<5; i++ ) try {
        System.gc();
        Thread.sleep(50);
    } catch(InterruptedException ex){}
}
Map<IdType, ValueType> cache = new HashMap<>();