Java GC不收集一个;“僵尸”;第二次反对
我试图创建一种机制,将对象缓存到内存中,以备将来使用,即使这些对象脱离上下文。将有一个并行的确定性过程,它将(通过一个唯一的ID)指示是否应该再次检索缓存的对象,或者它是否应该完全死亡。下面是一个最简单的示例,其中包含调试信息,使事情变得更简单: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
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方法将其从内存中完全删除
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<>();