了解Java内存管理

Java程序员知道JVM运行垃圾收集器,而System.gc()只是建议JVM运行垃圾收集器。 如果我们使用System.gc(),它不一定会立即运行GC。

如果我误解了Java的垃圾收集器,请纠正我。

除了依赖Java的垃圾收集器之外,是否有其他方式进行内存管理?
如果您打算通过某种有助于管理内存的编程实践来回答这个问题,请这样做。

关于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()

希望这有帮助。

下面是我在日子里写的小摘要(我从一些博客上偷了它,但我不记得从哪里来 – 所以没有参考,对不起)

  1. 在Java中没有手动方式进行垃圾收集。
  2. 为了垃圾收集,Java Heap分为三代。 这些是年轻一代,终身或老一代,以及彼尔姆地区。
  3. 新对象在年轻一代中创建,随后移至旧一代。
  4. 字符串池是在堆的Perm区域中创建的,垃圾收集可以在perm空间中进行,但依赖于JVM到JVM。
  5. 小垃圾收集用于将对象从Eden空间移动到Survivor 1和Survivor 2空间,Major集合用于将对象从年轻移动到终生。
  6. 每当主要垃圾收集发生应用程序时,线程在此期间停止,这将降低应用程序的性能和吞吐量。
  7. 在Java 6中,垃圾收集中几乎没有应用性能改进,我们通常使用JRE 1.6.20来运行我们的应用程序。
  8. JVM命令行选项-Xms-Xmx用于设置Java堆的起始大小和最大大小。 根据我的经验,此参数的理想比例为1:1或1:1.5,例如,您可以同时将–Xmx–Xms为1GB或-Xms设置为1.2 GB和1.8 GB。

命令行选项: -Xms: -Xmx:

只是添加到讨论中: 垃圾收集不是Java中唯一的内存管理forms

过去,在实现内存管理时,已经努力避免Java中的GC(请参阅Java的实时规范(RTSJ) )。 由于性能开销或GC引入的延迟,这些工作主要致力于Java中不适合的实时和嵌入式编程。

RTSJ的特点

  • 不朽和范围内存管理 – 请参阅下面的示例。
  • GC和Immortal / Scoped Memory可以与一个应用程序共存
  • RTSJ需要经过特殊修改的JVM。

RTSJ优势:

  • 低延迟,没有GC暂停
  • 提供可预测的性能,能够满足实时系统要求

为什么RTSJ失败/没有产生重大影响:

  • Scoped Memory概念很难编程,容易出错并且难以学习。
  • 实时GC算法的进展减少了GC暂停时间,使得实时GC取代了大多数实时应用程序中的RTSJ。 但是,Scoped Memories仍然用于不容忍延迟的地方。

范围内存代码示例(取自范围内存使用示例 ):

 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. } }); } } } 

这里,预先分配了一个名为LTMemory的ScopedMemory实现。 然后一个线程进入作用域内存,分配仅在计算期间所需的临时数据。 在计算结束后,线程离开作用域内存,立即使特定ScopedMemory的整个内容被清空。 没有引入延迟,在恒定时间内完成,例如可预测的时间,没有触发GC。

根据我的经验,在java中你应该依赖JVM本身提供的内存管理。

我在本主题中关注的重点是以您的用例可接受的方式配置它。 也许检查/理解JVM调优选项会很有用: http : //docs.oracle.com/cd/E15523_01/web.1111/e13814/jvm_tuning.htm

如果使用Java,则无法避免垃圾回收。 也许有一些不起眼的JVM实现,但我不知道。

经过适当调整的JVM不应要求任何System.gc()提示顺利运行。 您需要的确切调整在很大程度上取决于您的应用程序的function,但根据我的经验,我总是使用以下标志打开concurrent-mark-and-sweep选项: -XX:+UseConcMarkSweepGC 。 此标志允许JVM利用CPU中的额外内核来清除后台线程上的死内存。 它有助于大大减少执行垃圾收集时程序被强制暂停的时间。

好吧,GC总是在那里 – 你不能创建超出它的掌握的对象(除非你使用本机调用或分配一个直接字节缓冲区,但在后一种情况下你没有真正的对象,只是一堆的字节)。 也就是说,通过重用对象来规避GC是绝对可能的。 例如,如果您需要一堆ArrayList对象,您可以根据需要创建每个对象,并让GC处理内存管理; 或者你可以在完成后调用每个list.clear() ,并把它放到某个队列,其他人可以使用它。

标准的最佳实践是不要进行那种重用,除非你有充分的理由(即,你已经分析并看到分配+ GC是一个问题,并且重用对象修复了这个问题)。 它导致更复杂的代码,如果你弄错了,它实际上可以使GC的工作更难(因为GC如何跟踪对象)。

基本上,Java中的想法是你不应该处理内存,除非使用“new”来分配新对象,并确保在完成对象时没有任何对象的引用。

所有其余的都是故意留给Java Runtime,并且 – 也是故意 – 尽可能模糊地定义,以使JVM设计者能够最有效地自由地工作。

使用类比:您的操作系统为您管理硬盘空间的命名区域(称为“文件”)。 包括删除和重用您不想再使用的区域。 您不会绕过该机制,而是将其留给操作系统

您应该专注于编写清晰,简单的代码并确保您的对象正确完成。 这将为JVM提供最佳的工作条件。

你说System.gc()是对编译器的请求而不是命令是正确的。 但使用以下程序,您可以确保它发生。

 import java.lang.ref.WeakReference; public class GCRun { public static void main(String[] args) { String str = new String("TEMP"); WeakReference wr = new WeakReference(str); str = null; String temp = wr.get(); System.out.println("temp -- " + temp); while(wr.get() != null) { System.gc(); } } } 

我建议看看以下教程及其内容

这是一个由四部分组成的教程系列,用于了解Java中垃圾收集的基础知识:

  1. Java垃圾收集简介

  2. Java垃圾收集如何工作?

  3. Java垃圾收集器的类型

  4. 监控和分析Java垃圾收集

我发现本教程非常有帮助。

在不需要时“无效”引用是使对象符合垃圾收集条件的最佳方法。

收集垃圾的对象有4种方式。 1.一旦不再需要,将引用指向null。

 String s = new String("Java"); 

一旦不需要此String,就可以将其指向null。

 s = null; 

因此,s将有资格进行垃圾收集。

  1. 将一个对象指向另一个对象,这样两个引用指向同一个对象和一个对象都符合GC的条件。

    String s1 = new String(“Java”);

    String s2 = new String(“C ++”);

将来如果s2还需要指向s1那么;

 s1 = s2; 

然后具有“Java”的对象将有资格获得GC。

  1. 方法完成后,方法中创建的所有对象都符合GC的条件。 因此,一旦从线程的堆栈中销毁该方法,那么该方法中的相应对象将被销毁。

  2. 隔离岛是另一个概念,其中具有内部链接且没有外部链接引用的对象有资格进行垃圾收集。 垃圾收集的“隔离岛”

示例:下面是android中Camera类的方法。 了解开发人员如何在不需要时将mCameraSource指向null。 这是专家级代码。

 public void release() { if (mCameraSource != null) { mCameraSource.release(); mCameraSource = null; } } 

垃圾收集器如何工作?
垃圾收集由名为Garbage Collector的守护程序线程执行。 当有足够的可用内存时,此恶魔线程具有低优先级并且它在后台运行。 但是当JVM发现堆已满并且JVM想要回收一些内存时,它会增加垃圾收集器线程的优先级并调用Runtime.getRuntime.gc()方法,该方法搜索所有没有引用或空引用的对象。摧毁那些物体。