为什么Java总是消耗更多的内存?
所以我有一个小的客户端代码为什么Java总是消耗更多的内存?,java,memory,Java,Memory,所以我有一个小的客户端代码 public class Client { private static Socket socket; private static ObjectOutputStream out; public static void main(String[] args) { while (true) { try { if (socket != null) {
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) {
}
当一个变量实际超出范围时,垃圾收集器永远不会运行,或者你会把大部分时间花在GC代码上 取而代之的是(这是一个相当简单的过程),它会等到您使用的内存达到阈值,然后才开始释放内存
这就是你所看到的,你的内存消耗增长如此之慢,以至于需要很长时间才能达到下一个阈值并真正释放内存。当一个变量实际超出范围时,垃圾收集器永远不会运行,或者你会把大部分时间花在GC代码上 取而代之的是(这是一个相当简单的过程),它会等到您使用的内存达到阈值,然后才开始释放内存
这就是您所看到的,您的内存消耗增长非常缓慢,需要很长时间才能达到下一个阈值并实际释放内存。IO由套接字实现缓存,直到刷新为止。因此,要么您真的从套接字读取输入/输出(或者对流调用#flush()),要么您关闭套接字。IO由套接字实现缓存,直到刷新为止。因此,要么您真的从套接字读取输入/输出(或者对流调用#flush()),要么您关闭套接字。看起来您从未关闭套接字或刷新ObjectOutputStream。还请注意,Java垃圾收集基本上不是在您希望的时候发生,而是在垃圾收集器认为合适的时候发生。看起来您从未关闭套接字或刷新ObjectOutputStream。还要注意的是,Java垃圾收集基本上不是在您希望的时候发生的,而是在垃圾收集器认为合适的时候发生的。我不认为添加close是您的问题,因为我认为您试图做的是继续写入流。您是否尝试过退出.flush()。这将刷新内容,使其不再位于内存中 我不认为添加close是你的问题,因为我认为你试图做的是继续向流中写入内容。您是否尝试过退出.flush()。这将刷新内容,使其不再位于内存中 对我来说,逻辑本身就是罪魁祸首,不存在退出while循环的条件。
同样,没有刷新。对我来说,逻辑本身就是罪魁祸首,没有条件退出while循环。
同样没有刷新。因此,我已经为您的客户机编写了一个简单的测试服务器,现在我正在运行这两个服务器,并且内存使用似乎没有增加
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仍然保存着对字符串的引用 在半小时内,我们的程序消耗了大约2MB的内存(在64位虚拟机上)。这一次,它发送了18000个字符串。因此,每个字符串平均使用大约100字节的内存 每根弦的长度都在11到17个字符之间。由于StringBuilder的分配策略,后者(大约一半)实际上使用32个字符数组,前者使用16个字符数组。这些需要64或32字节+阵列开销(至少多12个字节,更有可能更多)。此外,字符串对象本身需要一些内存开销(对于类和我记得的字段,至少8+8+4+4+4=28,更可能是更多),因此每个字符串平均(至少)88字节。此外,ObjectOutputStream中可能存在一些开销,以在某些数据结构中维护这些对象 因此,损失并不比实际需要的多 啊,如果您不打算再次发送任何对象,如何避免ObjectOutputStream(以及相应的ObjectInputStream)存储这些对象的一个技巧是:每隔大约一千个字符串调用它的
重置
方法。
这是我在一个多小时后终止程序前的最后一个屏幕截图:
为了进行比较,我添加了名为reset,让程序再运行两个小时(还有一点):
它仍然像以前一样收集内存,但现在当我单击“performgc”时,它会清除所有内容并返回到以前的状态(略高于1MB)。(在堆的末尾也会这样做,但我不想等这么久。)因此,我为您的客户机编写了一个简单的测试服务器,我很高兴