强制显式删除 Java 对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2178028/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Force explicit deletion of a Java object
提问by Curious George
I'm working on a Java server that handles a LOT of very dense traffic. The server accepts packets from clients (often many megabytes) and forwards them to other clients. The server never explicitly stores any of the incoming/outgoing packets. Yet the server continually runs into OutOfMemoryException
exceptions.
我正在处理大量非常密集的流量的 Java 服务器。服务器接受来自客户端的数据包(通常为数兆字节)并将它们转发给其他客户端。服务器从不明确存储任何传入/传出数据包。然而,服务器不断遇到OutOfMemoryException
异常。
I added System.gc()
into the message passing component of the server, hoping that memory would be freed. Additionally, I set the heap size of the JVM to a gigabyte. I'm still getting just as many exceptions.
我添加System.gc()
到服务器的消息传递组件中,希望可以释放内存。此外,我将 JVM 的堆大小设置为 GB。我仍然收到同样多的异常。
So my question is this: how can I make sure that the megabyte messages aren't being queued indefinitely (despite not being needed)? Is there a way for me to call "delete" on these objects to guarantee they are not using my heap space?
所以我的问题是:如何确保兆字节消息不会无限期地排队(尽管不需要)?有没有办法让我对这些对象调用“删除”以保证它们不使用我的堆空间?
try
{
while (true)
{
int r = generator.nextInt(100);//generate a random number between 0 and 100
Object o =readFromServer.readObject();
sum++;
// if the random number is larger than the drop rate, send the object to client, else
//it will be dropped
if (r > dropRate)
{
writeToClient.writeObject(o);
writeToClient.flush();
numOfSend++;
System.out.printf("No. %d send\n",sum);
}//if
}//while
}//try
采纳答案by Michael Borgwardt
Looking at your code: are your ObjectInput/OutputStream
instances newly created each time a packet arrives or is sent, and if so, are they closed properly? If not, do you call reset()
after each read/write? The object stream classes keep a reference to all objects they have seen (in order to avoid resending the same object each time it is referred), preventing them from being garbage collected. I had that exact problem about 10 years ago - actually the first time I had to use a profiler to diagnose a memory leak...
查看您的代码:ObjectInput/OutputStream
每次数据包到达或发送时,您的实例是否都是新创建的,如果是,它们是否正确关闭?如果没有,您是否reset()
在每次读/写后调用?对象流类保持对它们所看到的所有对象的引用(以避免每次引用相同的对象时重新发送),防止它们被垃圾收集。大约 10 年前,我遇到了那个确切的问题——实际上是我第一次不得不使用分析器来诊断内存泄漏......
回答by Bozho
You can't call explicit garbage collection. But this is not the problem here. Perhaps you are storing references to these messages. Trace where they are handled and make sure no object holds reference to them after they are used.
您不能调用显式垃圾收集。但这不是这里的问题。也许您正在存储对这些消息的引用。跟踪它们被处理的位置,并确保在使用它们后没有对象持有对它们的引用。
To get a better idea of what the best practices are, read Effective Java, chapter 2- it's about "Creating and Destroying Objects"
要更好地了解最佳实践是什么,请阅读Effective Java,第 2 章- 它是关于“创建和销毁对象”
回答by BalusC
When JVM is on an edge of OutOfMemoryError
, it willrun the GC.
当JVM上的边缘OutOfMemoryError
,它会运行GC。
So calling System.gc()
yourself beforehand ain't going to fix the problem. The problem is to be fixed somewhere else. There are basically two ways:
所以System.gc()
事先给自己打电话并不能解决问题。问题是要在其他地方解决。基本上有两种方式:
- Write memory efficient code and/or fix memory leaks in your code.
- Give JVM more memory.
- 编写内存高效的代码和/或修复代码中的内存泄漏。
- 给JVM更多内存。
Using a Java Profilermay give a lot of information about memory usage and potential memory leaks.
使用Java Profiler可能会提供大量有关内存使用情况和潜在内存泄漏的信息。
Update: as per your edit with more information about the code causing this problem, have a look at Geoff Reedy's answer in this topicwhich suggests to use ObjectInputStream#readUnshared()
and ObjectOutputStream#writeUnshared()
instead. The (linked) Javadocs also explains it pretty well.
更新:根据您的编辑以及有关导致此问题的代码的更多信息,请查看本主题中Geoff Reedy 的答案,该答案建议使用ObjectInputStream#readUnshared()
和ObjectOutputStream#writeUnshared()
。(链接的)Javadocs 也很好地解释了它。
回答by BobMcGee
You cannot explicitly force deletion, but you CAN ensure that references to messages are not held by only keeping one direct reference in memory, and then using Reference objects to hold garbage-collectible references to it.
您不能显式地强制删除,但是您可以通过只在内存中保留一个直接引用,然后使用 Reference 对象来保存对它的垃圾回收引用来确保对消息的引用不被保留。
What about using a (small, bounded-size) queue for messages to process, then a secondary SoftReference queue which feeds to the first queue? This way you guarantee that processing will proceed BUT also that you won't get out of memory errors if messages are too big (the reference queue will get dumped in that case).
使用一个(小的、有界大小的)队列来处理消息,然后使用一个辅助的 SoftReference 队列来馈送第一个队列呢?通过这种方式,您可以保证处理将继续进行,但如果消息太大,您也不会出现内存不足错误(在这种情况下,引用队列将被转储)。
回答by Geoff Reedy
回答by fastcodejava
回答by Paolo
You need to find out if you are holding onto objects longer than necessary. The first step would be to get a profiler on the case and look at the heap and see why objects aren't being collected.
您需要查明您握住物体的时间是否超过了必要的时间。第一步是在案例上获取分析器并查看堆并查看为什么没有收集对象。
Although you've given the JVM 1GB, it may be that your young generation is too small if lots of objects are being created very quickly forcing them into older generations where they won't be removed as quickly.
尽管您已经给了 JVM 1GB,但如果大量对象被非常快速地创建,迫使它们进入不会被快速删除的老年代,那么您的年轻代可能太小了。
Some useful info on GC tuning: http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html
关于 GC 调整的一些有用信息:http: //java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html
回答by phisch
System.gc() is only a recommendation to the Java Virtual Machine. You call it and the JVM may or may not run the garbage collection.
System.gc() 只是对 Java 虚拟机的推荐。您调用它,JVM 可能会也可能不会运行垃圾收集。
The OutOfMemoryException may be caused by two things. Either you keep (unwanted) references to your objects or you are accepting to many packets.
OutOfMemoryException 可能由两件事引起。要么保留对对象的(不需要的)引用,要么接受许多数据包。
The first case can be analyzed by a profiler, where you try to find out how many references are still live. A good indication for a memory leek is growing memory consumption of your server. If every additional request makes your Java process grow a little, chances are you are keeping references somewhere (jconsole might be a good start)
第一种情况可以通过分析器进行分析,您可以在其中尝试找出仍然存在的引用数。内存韭菜的一个很好的迹象是服务器的内存消耗不断增加。如果每个额外的请求都使您的 Java 进程增长一点,那么您可能会在某处保留引用(jconsole 可能是一个好的开始)
If you are accepting more data than than you can handle, you will have to block additional requests until others are completed.
如果您接受的数据超出了您的处理能力,您将不得不阻止其他请求,直到其他请求完成。
回答by Michael Borgwardt
The server accepts packets from clients (often many megabytes) and forwards them to other clients.
服务器接受来自客户端的数据包(通常为数兆字节)并将它们转发给其他客户端。
Your code probably receives the "packets" completely before forwarding them. This means it needs enough memory to store all packets entirely until they've been forwarded completely, and when those packets are "many megabytes large" that means you need a lot of memory indeed. it also results in unnecessary latency.
您的代码可能会在转发它们之前完全接收“数据包”。这意味着它需要足够的内存来完全存储所有数据包,直到它们被完全转发,当这些数据包“有很多兆字节大”时,这意味着您确实需要大量内存。它还导致不必要的延迟。
It's possible that you have a memory leak as well, but if the above is true, this "store and forward" design is your biggest problem. You can probably cut memory usage by 95% if you redesign the app to not receive packets completely and instead stream them directly to the clients, i.e. read only a small part of the package at a time and transmit that to the clients immediately. It's not difficult to do this in a way that looks exactly the same to the clients as when you do store-and-forward.
您也可能存在内存泄漏,但如果上述情况属实,那么这种“存储和转发”设计就是您最大的问题。如果将应用程序重新设计为不完全接收数据包而是将它们直接流式传输到客户端,即一次仅读取包的一小部分并立即将其传输到客户端,您可能可以将内存使用量减少 95%。以一种在客户看来与进行存储转发时完全相同的方式来执行此操作并不困难。
回答by Steve B.
Manually triggering System.gc is not a good answer, as others have posted here. It's not guaranteed to run, and it triggers a full gc, which is likely to hang your server for a long time while it runs(>1 sec if you're giving your server a GB of ram, I've seen several-minute long pauses on larger systems). You could tune your gc which will certainly help, but not completely fix the problem.
手动触发 System.gc 不是一个好的答案,正如其他人在此处发布的那样。它不能保证运行,并且它会触发一个完整的 gc,这可能会在它运行时挂起你的服务器很长时间(如果你给你的服务器一 GB 内存,>1 秒,我见过几分钟在较大的系统上长时间停顿)。您可以调整您的 gc,这肯定会有所帮助,但不能完全解决问题。
If you're reading objects from one stream, and then writing them out to another, Then there's a point in which you're holding the entire object in memory. If these objects are, as you state, large, then that could be your problem. Try to rewrite your IO so that you read bytes from 1 stream and write them to another without ever explicitly holding the complete object (although I can't see how this would work with object serialization/deserialization if you need to verify/validate the objects).
如果您从一个流中读取对象,然后将它们写到另一个流中,那么在某一点上,您将整个对象保存在内存中。如果这些对象如您所说的那样大,那么这可能是您的问题。尝试重写您的 IO,以便您从 1 个流读取字节并将它们写入另一个流,而无需显式保存完整对象(尽管如果您需要验证/验证对象,我看不出这将如何与对象序列化/反序列化一起使用)。
回答by Aadith Ramia
just to add to all those previous replies : System.gc() is not a command to the JVM to initiate garbage collection..it is a meek direction and does not guarantee anything to happen. The JVM specification leaves it to the vendors to take a call on what needs to be done on gc calls. Vendors may even choose to do nothing at all!
只是添加到所有以前的答复中: System.gc() 不是 JVM 启动垃圾收集的命令..这是一个温和的方向,不保证会发生任何事情。JVM 规范让供应商来决定需要在 gc 调用上做什么。供应商甚至可能选择什么都不做!