Java GC花费太多时间
我正在用IntelliJ对Java堆进行一些测试,我得到以下错误:Java GC花费太多时间,java,garbage-collection,jvm,Java,Garbage Collection,Jvm,我正在用IntelliJ对Java堆进行一些测试,我得到以下错误: java.lang.OutOfMemoryError: GC overhead limit exceeded. 我知道当GC花费太多时间时会发生这种情况,但我不明白为什么GC会如此频繁地发生并花费这么多时间 设计: -Xms5m -Xmx5m import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Condition;
java.lang.OutOfMemoryError: GC overhead limit exceeded.
我知道当GC花费太多时间时会发生这种情况,但我不明白为什么GC会如此频繁地发生并花费这么多时间
设计:
-Xms5m -Xmx5m
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Example {
public static ReentrantLock lock = new ReentrantLock(true);
public static Condition writeCondition = lock.newCondition();
public static Condition monitorCondition = lock.newCondition();
public static void main(String[] args) throws Exception {
new Thread(new Monitor(lock, writeCondition, monitorCondition)).start(); // start a monitor thread
Map<Integer, Object> map = new HashMap<>();
for (int count = 0; count < 10000000; count++) {
lock.lock();
try {
// every time create a new Object, I will use monitor thread print current free heap size
monitorCondition.signal();
map.put(count, new Object());
writeCondition.await();
} finally {
lock.unlock();
}
}
for (Map.Entry entry : map.entrySet()) {
// keep reference to these Objects, so they will not be garbage collected.
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
}
class Monitor implements Runnable {
ReentrantLock lock;
Condition writeCondition;
Condition monitorCondition;
public Monitor(ReentrantLock lock, Condition writeCondition, Condition monitorCondition) {
this.lock = lock;
this.writeCondition = writeCondition;
this.monitorCondition = monitorCondition;
}
@Override
public void run() {
int count = 0;
while (true) {
lock.lock();
try {
writeCondition.signal();
long heapFreeSize = Runtime.getRuntime().freeMemory();
System.out.println(count + " times run monitor : ");
System.out.println("heapFreesize : " + heapFreeSize);
System.out.println();
count++;
monitorCondition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
60005 times run monitor :
heapFreesize:603896
Exception in thread "Thread-0" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.nio.CharBuffer.wrap(CharBuffer.java:373)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.newLine(PrintStream.java:545)
at java.io.PrintStream.println(PrintStream.java:696)
at hello.Monitor.run(Example.java:58)
at java.lang.Thread.run(Thread.java:748)
在主线程中,我重复创建新对象,然后将它们放在地图中。为了防止收集这些对象,我将在将所有对象放入地图后打印这些对象
在监视器线程中,每次创建新对象时,我都打印当前的空闲堆大小
运行时堆大小配置:
-Xms5m -Xmx5m
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Example {
public static ReentrantLock lock = new ReentrantLock(true);
public static Condition writeCondition = lock.newCondition();
public static Condition monitorCondition = lock.newCondition();
public static void main(String[] args) throws Exception {
new Thread(new Monitor(lock, writeCondition, monitorCondition)).start(); // start a monitor thread
Map<Integer, Object> map = new HashMap<>();
for (int count = 0; count < 10000000; count++) {
lock.lock();
try {
// every time create a new Object, I will use monitor thread print current free heap size
monitorCondition.signal();
map.put(count, new Object());
writeCondition.await();
} finally {
lock.unlock();
}
}
for (Map.Entry entry : map.entrySet()) {
// keep reference to these Objects, so they will not be garbage collected.
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
}
class Monitor implements Runnable {
ReentrantLock lock;
Condition writeCondition;
Condition monitorCondition;
public Monitor(ReentrantLock lock, Condition writeCondition, Condition monitorCondition) {
this.lock = lock;
this.writeCondition = writeCondition;
this.monitorCondition = monitorCondition;
}
@Override
public void run() {
int count = 0;
while (true) {
lock.lock();
try {
writeCondition.signal();
long heapFreeSize = Runtime.getRuntime().freeMemory();
System.out.println(count + " times run monitor : ");
System.out.println("heapFreesize : " + heapFreeSize);
System.out.println();
count++;
monitorCondition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
60005 times run monitor :
heapFreesize:603896
Exception in thread "Thread-0" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.nio.CharBuffer.wrap(CharBuffer.java:373)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.newLine(PrintStream.java:545)
at java.io.PrintStream.println(PrintStream.java:696)
at hello.Monitor.run(Example.java:58)
at java.lang.Thread.run(Thread.java:748)
测试代码:
-Xms5m -Xmx5m
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Example {
public static ReentrantLock lock = new ReentrantLock(true);
public static Condition writeCondition = lock.newCondition();
public static Condition monitorCondition = lock.newCondition();
public static void main(String[] args) throws Exception {
new Thread(new Monitor(lock, writeCondition, monitorCondition)).start(); // start a monitor thread
Map<Integer, Object> map = new HashMap<>();
for (int count = 0; count < 10000000; count++) {
lock.lock();
try {
// every time create a new Object, I will use monitor thread print current free heap size
monitorCondition.signal();
map.put(count, new Object());
writeCondition.await();
} finally {
lock.unlock();
}
}
for (Map.Entry entry : map.entrySet()) {
// keep reference to these Objects, so they will not be garbage collected.
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
}
class Monitor implements Runnable {
ReentrantLock lock;
Condition writeCondition;
Condition monitorCondition;
public Monitor(ReentrantLock lock, Condition writeCondition, Condition monitorCondition) {
this.lock = lock;
this.writeCondition = writeCondition;
this.monitorCondition = monitorCondition;
}
@Override
public void run() {
int count = 0;
while (true) {
lock.lock();
try {
writeCondition.signal();
long heapFreeSize = Runtime.getRuntime().freeMemory();
System.out.println(count + " times run monitor : ");
System.out.println("heapFreesize : " + heapFreeSize);
System.out.println();
count++;
monitorCondition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
60005 times run monitor :
heapFreesize:603896
Exception in thread "Thread-0" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.nio.CharBuffer.wrap(CharBuffer.java:373)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.newLine(PrintStream.java:545)
at java.io.PrintStream.println(PrintStream.java:696)
at hello.Monitor.run(Example.java:58)
at java.lang.Thread.run(Thread.java:748)
当可用内存不足时,GC会通过在堆中收集无法访问的对象来释放一些内存。检查对象是否无法访问不是一项容易的任务,需要一些时间
当您的所有(或大部分)对象仍在某个位置被引用时,这是浪费时间,因为没有回收内存。因此,当JVM意识到大量的运行时花费在运行GC而不是做一些有用的事情时,程序会在您指定的
-Xms5m-Xmx5m
参数JVM
中以OutOfMemoryError终止,因此堆大小设置为5MB
,即5242880字节
。现在,由于您已经执行了10000000
次循环,因此您正在Map
中分配10000000
类型为Integer
和Object
的对象。现在,如果您查看功能,您将意识到您在map
中为单个条目平均提供了0.524288
字节。但正如您所知,Integer
对象的大小大于4字节
(因为int
的大小是4字节
,Integer
包装int
类型)
因此,简而言之,您没有为JVM
堆分配足够的内存,这就是您获得异常的原因。java.lang.OutOfMemoryError:超出了GC开销限制。
注意:查看程序的以下输出后,我意识到单个条目占用了848字节的内存(请参见下面的计算)。因此,您将需要至少10000000*848=848000000字节
,即8088 MB
内存(物理或虚拟内存):
尝试将更多的堆分配给JVM
,将不会出现此错误。有关Java HotSpot VM选项的更多信息,请参见和
计算:在提供的输出(我在上面发布)中,正在打印可用内存。现在,如果您进行计算,您将发现在向映射添加一个条目后,内存之间的差异是848
。因此,我可以说,map
中的单个条目(不是单个对象,因为有两个对象-整数类和对象类)正在消耗848 byres
,例如32344-31496=848
<代码>31496-30648=848
<代码>30648-29800=848,依此类推
还请注意,如果您没有使用相同的值设置-Xms
和-Xmx
开关(因为我在执行示例运行时没有使用这些开关),那么JVM
将根据需要增加或减少堆大小
注意:由于使用了writeCondition.signal(),您的代码在逻辑上似乎不正确代码>和监视器条件.await()代码>不在sync
中。程序正在无限地等待
。我认为它在最后被卡住了。在这段代码中,您试图将1000万个对象(通常每个8字节)放入5Mb堆中(甚至不计算运行时和您使用的其他元素的开销)。这是行不通的。@gpeche是的,事实上我希望OutOfMemoryError
。但我不明白GC为什么要花这么多时间。因为我创建的对象不应该被垃圾收集。有趣的阅读:@user6690200 read,它引用的JVM似乎不够聪明,无法快速找到我创建的所有对象,它只是重复检查这些对象。我说得对吗?是的,这是正确的。然而,这个问题本身就很难解决;所以这不是一个不够聪明的问题。@user6690200“JVM不够聪明”是什么意思?您将使用新创建的对象填充贴图,直到内存已满。这与聪明无关。没有要收集的对象。@Holger我的意思是,JVM在查找无法访问的对象上花费的时间太多了吗。事实上,没有不可访问的对象。@Holger和,抛出OutOfMemoryError时,似乎仍有可用的堆空间,我希望在没有可用的堆空间之前不会抛出此错误……谢谢。我将循环更改为60000,这足够小,可以分配到5MB空间。我仍然会犯同样的错误。有什么方法可以降低GC所用时间的限制吗?@user6690200对于map
变量中的60000
条目,您至少需要48.6MB
内存。因此,我建议将JVM
参数设置为-Xms60m-Xmx60m
。请注意,48.6 MB是map
保存条目所需的内存。JVM
将需要更多的内存来执行它的其他任务。非常感谢!我发现当使用60000和5MB堆大小时,测试结果可能会有所不同,有时所有60000个条目都可以放入map,有时失败。请问,为什么你说单个对象需要848
字节?@user6690200在提供的输出中(由我在回答中发布),可用内存正在打印。现在,如果您进行计算,您将发现在map
中添加一个条目后,内存之间的差异是848
。因此,我可以说,单个条目(不是单个对象,因为有2个