Java堆内存的10个要点
fmms 13年前
<p> 当我开始学习 Java 编程时,我不知道什么是堆内存或堆空间,我甚至不知道当对象创建时,它们被放在了哪里。当我开始正式写一些程序后,我会经常遇到 java.lang.outOfMemoryError 的报错,之后我才开始关注什么是堆内存或者说堆空间(heap space)。对大多数<a title="程序员的本质" href="/misc/goto?guid=4958202204547787659">程序员</a>都经历过这样的过程,因为学习一种语言是非常容易来的,但是学习基础是非常难的,因为没有什么特定的流程让你学习编程的每个基础,使你发觉编程的秘诀。对于程序员来说,知道堆空间,设置堆空间,处理堆空间的 outOfMemoryError 错误,分析 heap dump 是非常重要的。这个关于 Java 堆的教程是给我刚开始学编程的兄弟看的。如果你知道这个基础知识或者知道底层发生了什么,当然可能帮助不是那么大。除非你知道了对象被创建在堆中,否则你不会意识到 OutOfMemoryError 是发生在堆空间中的。我尽可能的将我所知道的所有关于堆的知识都写下来了,也希望你们能够尽可能多的贡献和分享你的知识,以便可以让其他人也受益。</p> <p> <strong>Java 中的堆空间是什么?</strong></p> <p> 当 Java 程序开始运行时,JVM 会从操作系统获取一些内存。JVM 使用这些内存,这些内存的一部分就是堆内存。堆内存通常在存储地址的底层,向上排列。当一个对象通过 new 关键字或通过其他方式创建后,对象从堆中获得内存。当对象不再使用了,被当做垃圾回收掉后,这些内存又重新回到堆内存中。要学习垃圾回收,请阅读”Java 中垃圾回收的工作原理”。</p> <p> <strong>如何增加 Java 堆空间</strong></p> <p> 在大多数 32 位机、Sun 的 JVM 上,Java 的堆空间默认的大小为 128MB,但也有例外,例如在 32 未 Solaris 操作系统(SPARC 平台版本)上,默认的最大堆空间和起始堆空间大小为 -Xms=3670K 和 -Xmx=64M。对于 64 位操作系统,一般堆空间大小增加约 30%。但你使用 Java 1.5 的 throughput 垃圾回收器,默认最大的堆大小为物理内存的四分之一,而起始堆大小为物理内存的十六分之一。要想知道默认的堆大小的方法,可以用默认的设置参数打开一个程序,使用 JConsole (JDK 1.5 之后都支持)来查看,在 VM Summary 页面可以看到最大的堆大小。</p> <p> 用这种方法你可以根据你的程序的需要来改变堆内存大小,我强烈建议采用这种方法而不是默认值。如果你的程序很大,有很多对象需要被创建的话,你可以用-Xms and -Xmx 这两个参数来改变堆内存的大小。Xms 表示起始的堆内存大小,Xmx 表示最大的堆内存的大小。另外有一个参数 -Xmn,它表示 new generation(后面会提到)的大小。有一件事你需要注意,你不能任意改变堆内存的大小,你只能在启动 JVM 时设定它。</p> <p> <strong>堆和垃圾回收</strong></p> <p> 我们知道对象创建在堆内存中,垃圾回收这样一个进程,它将已死对象清除出堆空间,并将这些内存再还给堆。为了给垃圾回收器使用,堆主要分成三个区域,分别叫作 New Generation,Old Generation 或叫 Tenured Generation,以及 Perm space。New Generation 是用来存放新建的对象的空间,在对象新建的时候被使用。如果长时间还使用的话,它们会被垃圾回收器移动到 Old Generation (或叫 Tenured Generation)。Perm space 是 JVM 存放 Meta 数据的地方,例如类,方法,字符串池和类级别的详细信息。你可以查看“Java 中垃圾回收的工作原理”来获得更多关于堆和垃圾回收的信息。</p> <p style="text-align:center;"><a><img title="java" alt="java" src="https://simg.open-open.com/show/f1776abfdc627aa53ac9785245d82a9a.jpg" width="480" height="320" /></a></p> <p> <strong>Java 堆中的 OutOfMemoryError 错误</strong></p> <p> 当 JVM 启动时,使用了-Xms 参数设置的对内存。当程序继续进行,创建更多对象,JVM 开始扩大堆内存以容纳更多对象。JVM 也会使用垃圾回收器来回收内存。当快达到-Xmx 设置的最大堆内存时,如果没有更多的内存可被分配给新对象的话,JVM 就会抛出 java.lang.outofmemoryerror,你的程序就会当掉。在抛出 OutOfMemoryError 之前,JVM 会尝试着用垃圾回收器来释放足够的空间,但是发现仍旧没有足够的空间时,就会抛出这个错误。为了解决这个问题,你需要清楚你的程序对象的信息,例如,你创建了哪些对象,哪些对象占用了多少空间等等。你可以使用 profiler 或者堆分析器来处理 OutOfMemoryError 错误。”java.lang.OutOfMemoryError: Java heap space”表示堆没有足够的空间了,不能继续扩大了。”java.lang.OutOfMemoryError: PermGen space”表示 permanent generation 已经装满了,你的程序不能再装在类或者再分配一个字符串了。</p> <p> <strong>Java Heap dump</strong></p> <p> Heap dump 是在某一时间对 Java 堆内存的快照。它对于分析堆内存或处理内存泄露和 Java.lang.outofmemoryerror 错误是非常有用的。在 JDK 中有一些工具可以帮你获取 heap dump,也有一些堆分析工具来帮你分析 heap dump。你可以用“jmap”来获取 heap dump,它帮你创建 heap dump 文件,然后,你可以用“jhat”(堆分析工具)来分析这些 heap dump。</p> <p> <strong>Java 堆内存(heap memory)的十个要点</strong></p> <p> 1. Java 堆内存是操作系统分配给 JVM 的内存的一部分。</p> <p> 2. 当我们创建对象时,它们存储在 Java 堆内存中。</p> <p> 3. 为了便于垃圾回收,Java 堆空间分成三个区域,分别叫作 New Generation, Old Generation 或叫作 Tenured Generation,还有 Perm Space。</p> <p> 4. 你可以通过用 JVM 的命令行选项 -Xms, -Xmx, -Xmn 来调整 Java 堆空间的大小。不要忘了在大小后面加上”M”或者”G”来表示单位。举个例子,你可以用 -Xmx256m 来设置堆内存最大的大小为 256MB。</p> <p> 5. 你可以用 JConsole 或者 Runtime.maxMemory (), Runtime.totalMemory (), Runtime.freeMemory ()来查看 Java 中堆内存的大小。</p> <p> 6. 你可以使用命令“jmap”来获得 heap dump,用“jhat”来分析 heap dump。</p> <p> 7. Java 堆空间不同于栈空间,栈空间是用来储存调用栈和局部变量的。</p> <p> 8. Java 垃圾回收器是用来将死掉的对象(不再使用的对象)所占用的内存回收回来,再释放到 Java 堆空间中。</p> <p> 9. 当你遇到 java.lang.outOfMemoryError 时,不要紧张,有时候仅仅增加堆空间就可以了,但如果经常出现的话,就要看看 Java 程序中是不是存在内存泄露了。</p> <p> 10. 请使用 Profiler 和 Heap dump 分析工具来查看 Java 堆空间,可以查看给每个对象分配了多少内存。</p> <p> 原文链接:<a href="/misc/goto?guid=4958329985198236710" rel="nofollow" target="_blank">java revisited</a> 编译:<a href="/misc/goto?guid=4958185140659301754">伯乐</a>在线 - <a href="/misc/goto?guid=4958329986751347617" target="_blank">唐小娟</a></p>