了解Java内存管理

时间:2012-12-13 07:28:40

标签: java memory-management memory-leaks garbage-collection jvm

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

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

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

10 个答案:

答案 0 :(得分:15)

关于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 :(得分:14)

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

  1. 在Java中没有手动方式进行垃圾收集。
  2. Java堆为了垃圾收集而分为三代。这些是年轻一代,终身或老一代,以及彼尔姆地区。
  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。
  9. 命令行选项:-Xms:<min size> -Xmx:<max size>

答案 2 :(得分:7)

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

过去,在实施内存管理时,已经努力避免Java中的GC(参见Real-time Specification for Java (RTSJ))。由于性能开销或GC引入的延迟,这些工作主要致力于使用GC不适合的Java实时和嵌入式编程。

RTSJ特征

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

RTSJ优势:

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

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

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

范围内存代码示例(取自An Example of Scoped Memory Usage):

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。

答案 3 :(得分:4)

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

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

答案 4 :(得分:4)

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

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

答案 5 :(得分:3)

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

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

答案 6 :(得分:3)

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

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

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

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

答案 7 :(得分:3)

你说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();
        }
    }
}

答案 8 :(得分:2)

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

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

  1. Java垃圾收集简介

  2. Java垃圾收集的工作原理是什么?

  3. Java垃圾收集器的类型

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

  5. 我发现This tutorial非常有帮助。

答案 9 :(得分:1)

&#34;在不需要时对引用进行Nullify&#34;是使对象符合垃圾收集条件的最佳方法。

可以通过4种方式收集垃圾。 1.一旦不再需要,将引用指向null。

String s = new String("Java");

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

s = null;

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

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

    String s1 = new String(&#34; Java&#34;);

    String s2 = new String(&#34; C ++&#34;);

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

    s1 = s2;
    

    然后该对象具有&#34; Java&#34;将有资格获得GC。

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

    2. 隔离岛是另一个概念,其中具有内部链接且没有引用的外部链接的对象符合垃圾收集的条件。 "Island of isolation" of Garbage Collection

    3. 实施例: 下面是android中Camera类的方法。查看开发人员如何在不需要时将mCameraSource指向null。这是专家级代码。

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

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