所以我有这个小客户端代码
public class Client {
private static Socket socket;
private static ObjectOutputStream out;
public static void main(String[] args) {
while (true) {
try {
if (socket != null) {
out.writeObject("Hello...");
Thread.sleep(1500);
} else {
socket = new Socket("myhost", 1234);
out = new ObjectOutputStream(socket.getOutputStream());
System.out.println("connected to server");
}
} catch (final Exception e) {
//set socket to null for reconnecting
}
}
}
}
让我感到困惑的是,当我用javaw.exe运行代码时,我发现java每2-3秒吃掉大约10kb的内存。因此,内存使用量不断增长和增长...
java真的那么糟糕还是有别的错?
我在while循环中运行了这段代码一段时间,内存使用量增加了1000 kb。 java gargabe在使用后收集'tmp'变量不是吗?
try {
if (socket == null) {
final Socket tmp = new Socket("localhost", 1234);
if (tmp != null) {
socket = tmp;
}
Thread.sleep(100);
}
} catch (final Exception e) {
}
答案 0 :(得分:8)
所以,我为你的客户编写了一个简单的测试服务器,我现在正在运行它们,并且内存使用似乎没有增加。
import java.net.*;
import java.io.*;
/**
* example class adapted from
* http://stackoverflow.com/questions/5122569/why-is-java-constantly-eating-more-memory
*/
public class Client {
private static Socket socket;
private static ObjectOutputStream out;
private static void runClient() {
while (true) {
try {
if (socket != null) {
out.writeObject("Hello...");
Thread.sleep(100);
System.out.print(",");
} else {
socket = new Socket("localhost", 1234);
out = new ObjectOutputStream(socket.getOutputStream());
System.out.println("connected to server");
}
} catch (final Exception e) {
//set socket to null for reconnecting
e.printStackTrace();
return;
}
}
}
private static void runServer() throws IOException{
ServerSocket ss = new ServerSocket(1234);
Socket s = ss.accept();
InputStream in = s.getInputStream();
byte[] buffer = new byte[500];
while(in.read(buffer) > 0) {
System.out.print(".");
}
}
public static void main(String[] args)
throws IOException
{
if(args.length > 0) {
runServer();
}
else {
runClient();
}
}
}
你在做什么不同?
所以,我对这个程序的内存使用情况看起来更详细,并且发现这是一个有用的工具,隐藏在我系统开发菜单中的“Java监视和管理控制台”: - )
以下是运行客户端程序一段时间内的内存使用情况的截图(每发送一个对象100毫秒,请记住)...
我们可以看到内存使用量有锯齿曲线 - 它是线性增加的,然后是垃圾收集,它正在降低到基本用法。在一些初始阶段之后,VM更频繁地进行GC(因此更快)。现在,没问题。
这是一个变体程序,我总是不发送相同的字符串,但每次都发送不同的字符串:
private static void runClient() {
int i = 0;
while (true) {
try {
i++;
if (socket != null) {
out.writeObject("Hello " + i + " ...");
Thread.sleep(100);
System.out.print(",");
(其余如上所述)。我认为这需要更多内存,因为ObjectOutputStream必须记住已经发送了哪些对象,以便能够重新使用它们的标识符。
但不,它看起来很相似:
39到40之间的微小不规则是由“执行GC”按钮在这里制作的手动完整GC - 但它没有太大变化。
我让最后一个程序运行一段时间,现在我们看到ObjectOutputStream仍然保持对我们的字符串的引用......
在半小时内,我们的程序吃了大约2 MB的内存(在64位VM上)。在这个时候,它发送了18000个字符串。因此,每个字符串平均使用大约100个字节的内存。
每个字符串长度在11到17个字符之间。由于StringBuilder的分配策略,后者(大约一半)实际上使用32-char-arrays,前者为16-char-arrays。这些需要64或32个字节+数组开销(至少12个字节,更可能更多)。此外,String对象本身需要一些内存开销(至少8 + 8 + 4 + 4 + 4 = 28用于类和我记忆的字段,更可能更多),因此我们平均每个字符串(至少)有88个字节。此外,ObjectOutputStream中可能存在一些开销,以便在某些数据结构中维护这些对象。
因此,实际上并不需要更多的损失。
啊,如果您不打算再次发送任何对象,如何避免存储对象的ObjectOutputStream(以及相应的ObjectInputStream)的一个提示:每次调用其reset
方法几千串左右。
这是我杀了程序之前的最后一次截图,经过一个多小时后:
为了比较,我添加了名为reset
并让程序再运行两个小时(以及一点点):
它仍然像以前一样收集内存,但现在当我点击“执行GC”时,它会清除所有内容并返回之前的状态(仅略高于1 MB)。 (在Heap结束时它也会这样做,但我不想等这么久。)
答案 1 :(得分:2)
当变量实际超出范围,或者你将大部分时间花在GC代码上时,垃圾收集器永远不会运行。
它的作用(这是一个非常简化)是它等待你的内存使用达到一个阈值,然后才开始释放内存。
这就是你所看到的,你的内存消耗增长如此之慢,以至于需要很长时间才能达到下一个阈值并实际释放内存。
答案 2 :(得分:2)
我不认为添加关闭是你的问题,因为我认为你想要做的就是继续写入流。你试过out.flush()
吗?这会刷新内容,使其不再存在于内存中。
答案 3 :(得分:1)
看起来你永远不会关闭Socket或刷新ObjectOutputStream。另请注意,Java垃圾收集基本上不是在您需要它时,而是在垃圾收集器看起来合适时。
答案 4 :(得分:0)
IO被Socket实现缓存,直到刷新为止。所以要么你真的从套接字读取输入/输出(或在你的流上调用#flush()),要么关闭套接字。
答案 5 :(得分:0)
对我来说,逻辑本身就是罪魁祸首,没有条件从while循环中走出来。 再没有冲洗。
答案 6 :(得分:0)
ObjectOutputStream缓存了您发送的每个对象,以防您再次发送它。要清除此项,您需要调用reset()
方法
BTW:10 KB的内存价值约为0.1美分。你最低工资的一分钟时间是这个时间的100倍。我建议你考虑一下你最好的时间用途。重置将忽略已写入流的任何对象的状态。状态重置为与新的ObjectOutputStream相同。流中的当前点标记为重置,因此相应的ObjectInputStream将在同一点重置。先前写入流的对象将不会被称为流中的对象。它们将再次写入流中。