Java 程序员知道 JVM 运行垃圾收集器,而 System.gc() 只是对 JVM 运行垃圾收集器的建议。如果我们使用 System.gc() 不一定会立即运行 GC。
如果我误解了 Java 的垃圾收集器,请纠正我。
除了依赖 Java 的垃圾收集器之外,还有其他方式进行内存管理吗?
如果您打算通过某种有助于管理内存的编程实践来回答这个问题,请这样做。
Java 程序员知道 JVM 运行垃圾收集器,而 System.gc() 只是对 JVM 运行垃圾收集器的建议。如果我们使用 System.gc() 不一定会立即运行 GC。
如果我误解了 Java 的垃圾收集器,请纠正我。
除了依赖 Java 的垃圾收集器之外,还有其他方式进行内存管理吗?
如果您打算通过某种有助于管理内存的编程实践来回答这个问题,请这样做。
下面是我以前写的小总结(我从某个博客上偷来的,但我不记得从哪里来的——所以没有参考,抱歉)
-Xms
,-Xmx
用于设置 Java Heap 的起始大小和最大大小。根据我的经验,此参数的理想比例是 1:1 或 1:1.5,例如,您可以同时拥有–Xmx
1GB–Xms
或 –Xms 1.2 GB 和 1.8 GB。命令行选项:-Xms:<min size> -Xmx:<max size>
关于 Java 内存管理要记住的最重要的事情是“取消”您的引用。
只有没有被引用的对象才会被垃圾回收。
例如,以下代码中的对象永远不会被收集,您的内存将被填满,只是什么都不做。
List objs = new ArrayList();
for (int i = 0; i < Integer.MAX_VALUE; i++) objs.add(new Object());
但是如果你不引用那些对象......你可以尽可能多地循环而不会出现内存问题。
List objs = new ArrayList();
for (int i = 0; i < Integer.MAX_VALUE; i++) new Object();
所以无论你做什么,确保你删除对不再使用的对象的引用(设置引用null
或清除集合)。
垃圾收集器何时运行最好留给 JVM 来决定。好吧,除非您的程序即将开始执行使用大量内存且速度至关重要的事情,否则您可能会建议 JVM 在进入之前运行 GC,因为您可能会收集垃圾并继续使用额外的内存。否则,我个人认为没有理由跑步System.gc()
。
希望这可以帮助。
只是为了增加讨论:垃圾收集不是 Java 中内存管理的唯一形式。
过去,在实现内存管理时,Java 中一直在努力避免 GC(请参阅Java 实时规范 (RTSJ))。这些努力主要致力于 Java 中的实时和嵌入式编程,GC 不适合 - 由于性能开销或 GC 引入的延迟。
RTSJ 特性
RTSJ 优势:
为什么 RTSJ 失败/没有产生重大影响:
范围内存代码示例(取自范围内存使用示例):
import javax.realtime.*;
public class ScopedMemoryExample{
private LTMemory myMem;
public ScopedMemoryExample(int Size) {
// initialize memory
myMem = new LTMemory(1000, 5000);
}
public void periodicTask() {
while (true)) {
myMem.enter(new Runnable() {
public void run() {
// do some work in the SCOPED MEMORY
new Object();
...
// end of the enter() method, the scoped Memory is emptied.
}
});
}
}
}
在这里,预先分配了一个称为 ScopedMemory 的实现LTMemory
。然后一个线程进入作用域内存,分配仅在计算期间需要的临时数据。计算结束后,线程离开作用域内存,立即清空特定作用域内存的全部内容。没有引入延迟,在恒定时间内完成,例如可预测的时间,没有触发 GC。
根据我的经验,在 java 中你应该依赖 JVM 本身提供的内存管理。
我在本主题中关注的重点是以您的用例可接受的方式对其进行配置。也许检查/理解 JVM 调整选项会很有用: http: //docs.oracle.com/cd/E15523_01/web.1111/e13814/jvm_tuning.htm
如果您使用 Java,则无法避免垃圾收集。也许有一些晦涩的JVM实现可以做到,但我不知道。
经过适当调整的 JVM 不需要任何 System.gc() 提示即可顺利运行。您需要的确切调整很大程度上取决于您的应用程序的功能,但根据我的经验,我总是使用以下标志打开并发标记和扫描选项-XX:+UseConcMarkSweepGC
:此标志允许 JVM 利用 CPU 中的额外内核来清理后台线程上的死内存。它有助于大大减少执行垃圾收集时程序被强制暂停的时间。
好吧,GC 总是在那里——你不能创建超出它掌握范围的对象(除非你使用本机调用或分配一个直接字节缓冲区,但在后一种情况下,你并没有真正的对象,只是一堆字节)。也就是说,绝对有可能通过重用对象来规避 GC。例如,如果您需要一堆ArrayList
对象,您可以根据需要创建每个对象,并让 GC 处理内存管理;或者您可以list.clear()
在完成后调用每个人,然后将其放入其他人可以使用的队列中。
标准的最佳实践是不要进行这种重用,除非您有充分的理由(即,您已经分析并看到分配 + GC 是一个问题,并且重用对象可以解决该问题)。它会导致更复杂的代码,如果你弄错了,它实际上会使 GC 的工作变得更加困难(因为 GC 跟踪对象的方式)。
基本上,Java 中的想法是,您不应该处理内存,除非使用“new”来分配新对象并确保在使用完对象后没有对对象的引用。
其余的一切都故意留给 Java 运行时,并且——也是故意的——尽可能模糊地定义,以允许 JVM 设计人员在有效地这样做时拥有最大的自由。
打个比方:您的操作系统为您管理命名的硬盘空间区域(称为“文件”)。包括删除和重用您不想再使用的区域。您不会绕过该机制,而是将其留给操作系统
您应该专注于编写清晰、简单的代码,并确保您的对象得到正确处理。这将为 JVM 提供可能的最佳工作条件。
您说这System.gc()
是对编译器的请求而不是命令是正确的。但是使用下面的程序,您可以确保它发生。
import java.lang.ref.WeakReference;
public class GCRun {
public static void main(String[] args) {
String str = new String("TEMP");
WeakReference<String> wr = new WeakReference<String>(str);
str = null;
String temp = wr.get();
System.out.println("temp -- " + temp);
while(wr.get() != null) {
System.gc();
}
}
}
我建议看看以下教程及其内容
这是一个由四部分组成的教程系列,用于了解垃圾收集的基础知识Java
:
Java垃圾回收介绍
Java 垃圾收集是如何工作的?
Java 垃圾收集器的类型
监控和分析 Java 垃圾收集
我发现本教程非常有帮助。
在不需要时“取消”引用是使对象符合垃圾收集条件的最佳方法。
有 4 种方法可以对对象进行垃圾收集。
一旦不再需要,将引用指向 null。
字符串 s = new String("Java"); 一旦不需要此字符串,您可以将其指向 null。
s = 空;因此,s 将有资格进行垃圾收集。
将一个对象指向另一个对象,以便两个引用都指向同一个对象,并且其中一个对象符合 GC 条件。
字符串 s1 = new String("Java");
字符串 s2 = new String("C++");
将来如果 s2 也需要指向 s1 则;
s1 = s2;
然后具有“Java”的对象将有资格进行 GC。
一旦方法完成,在方法中创建的所有对象都有资格进行 GC。因此,一旦方法从线程的堆栈中被销毁,那么该方法中的相应对象将被销毁。
Island of Isolation 是另一个概念,其中具有内部链接且没有外部引用链接的对象有资格进行垃圾收集。 垃圾回收的“孤岛”
示例:下面是android中Camera类的一个方法。了解开发人员如何在不需要时将 mCameraSource 指向 null。这是专家级代码。
public void release() {
if (mCameraSource != null) {
mCameraSource.release();
mCameraSource = null;
}
}
垃圾收集器如何工作?
垃圾收集由称为垃圾收集器的守护线程执行。当有足够的内存可用时,这个恶魔线程的优先级很低,它在后台运行。但是当 JVM 发现堆已满并且 JVM 想要回收一些内存时,它会增加垃圾收集器线程的优先级并调用 Runtime.getRuntime.gc() 方法来搜索所有没有引用或空引用的对象和销毁那些对象。