java.lang.OutOfMemoryError (OOM)解密 & Java heap dumps 解析 (一)

Unveiling the java.lang.Out OfMemoryError
And dissecting Java heap dumps


When we encounter a java.lang.OutOfMemoryError, we often find that Java heap dumps, along with other artifacts, are generated by the Java Virtual Machine. If you feel like jumping right into a Java heap dump when you get a java.lang.OutOfMemoryError, don't worry, it's a normal thought. You may be able to discover something serendipitously, but it's not always the best idea to analyze Java heap dumps, depending on the situation you are facing. We first need to investigate the root cause of the java.lang.OutOfMemoryError.

         當我們遇到java.lang.OutOfMemoryError ,我們常常會發現JVM會進行Java堆轉儲,以及由Java虛擬機生成的其他文件。當你得到一個java.lang.OutOfMemoryError時,如果你感覺剛好JVM在進行Java堆轉儲 ,不要擔心,這是正常的想法。你也許可以僥倖發現一些信息,但這並不總是分析Java堆轉儲的最好方式,因爲這取決於你所面對的實際場景。我們首先需要調查java.lang.OutOfMemoryError的根本原因。

Only after the root cause is identified can we decide whether or not to analyze Java heap dumps. What is a java.lang.OutOfMemoryError? Why in the world does it occur? Let's find out.

      只有我們確定OOM的根本原因後,我們才能決定是否要進行Java堆轉儲分析。那麼什麼是 java.lang.OutOfMemoryError?什麼情況下會發生 java.lang.OutOfMemoryError?下面我們來探究下。

What Is a java.lang.OutOfMemoryError?
A java.lang.OutOfMemoryError is a subclass of java.lang.VirtualMachineError that is thrown when the Java Virtual Machine is broken or has run out of resources that are necessary to continue the operation of the Java Virtual Machine. Obviously, memory is the exhausted resource for a java.lang.OutOfMemoryError, which is thrown when the Java Virtual Machine cannot allocate an object due to memory constraints. Unfortunately, the Java specification of java.lang.OutOfMemoryError does not elaborate further on what kind of memory it's talking about.

        java.lang.OutOfMemoryError 是java.lang.VirtualMachineError的一個子類,當JVM崩潰或者當使得JVM繼續運行的必備資源耗盡時會拋出java.lang.OutOfMemoryError異常。很明顯,對於java.lang.OutOfMemoryError來說,內存則是被耗盡的資源,由於內存耗盡限制,JVM無法爲一個對象分配內存。

        不幸的是,Java規範並沒有進一步說明java.lang.OutOfMemoryError涉及的是哪種內存。

There are six different types of runtime data areas, or memory areas, in the Java Virtual Machine (seeFigure 1).

在JVM裏面有6種不同類型的運行時數據區或內存區,如下:

  1. Program Counter Register (程序計數器寄存器
  2. Java Virtual Machine Stack Java虛擬機堆棧)
  3. Heap (堆)
  4. Method Area (方法區)
  5. Runtime Constant Pool (運行時常連池)
  6. Native Method Stack (本地方法棧)

The Program Counter Register, also known as the pc register, stores the address of the Java byte code instruction that is currently being executed (just like the processor register in your central processing unit of the device from which you are reading or printing this article). You will not see a java.lang.OutOfMemoryError from the pc register since a program counter is not conventionally considered as a memory.

       程序計數器寄存器,也被稱爲PC寄存器,存儲當前正在執行(就像在從中你正在閱讀或打印這篇文章您設備的中央處理單元的處理器寄存器)的Java字節碼指令的地址你不會看到一個由於PC寄存器錯誤產生java.lang.OutOfMemoryError,因爲程序計數器通常不被視爲內存。

Java Virtual Machine Stacks contain frames where data, return values, and partial execution results are stored. Java Virtual Machine Stacks can be expanded during runtime. If there's not enough memory for the expansion of an existing Java Virtual Machine stack, or for the creation of a new Java Virtual Machine stack for a new thread, the Java Virtual Machine will throw a java.lang.OutOfMemoryError.

        Java虛擬機堆棧包含一系列的幀,也就是數據、返回值和部分執行結果存儲的地方。Java虛擬機堆棧可以在運行時擴展。如果沒有足夠的內存用於現有的Java虛擬機堆的擴大,或爲一個新的線程創建一個Java虛擬機棧,此時, Java虛擬機將會拋出一個java.lang.OutOfMemoryError 。

The Heap is where instances of Java classes and arrays are allocated. A java.lang.OutOfMemoryError will be thrown when there is not enough memory available for instances of Java classes or arrays.

        堆是爲Java類和數組的實例分配內存的地方。當沒有足夠的可用於Java類或數組的實例的內存分配時,將會拋出java.lang.OutOfMemoryError。

The Method Area stores class-related information, the runtime constant pool, for instances, the code for methods and constructors, and field/method data. If there's not enough memory in the method area, you will encounter java.lang.OutOfMemoryError.

        方法區存儲類相關的信息、運行時的常量池;例如,對於類實例來說,就是方法和構造方法以及字段或方法數據的代碼。如果方法區內沒有足夠的內存,你同樣會碰到java.lang.OutOfMemoryError 異常。

The Runtime Constant Pool contains constants such as field references and literals. A java.lang.OutOfMemoryError will be thrown when not enough memory is available for the construction of the runtime constant pool area.

        運行時常量池包含像字段引用以及文本這樣的常量。當沒有足夠的內存可用於運行時常量池區的構建時,也會拋出java.lang.OutOfMemoryError。

Native Memory Stacks store conventional stacks, also known as C stacks, to support native methods that are written in a non-Java language such as C/C++. Native memory stacks can be expanded during runtime. If there's not enough memory for the expansion of an existing native memory stack or for the creation of a new native memory stack for a new thread, you would see a java.lang.OutOfMemoryError.

        本地內存棧,存儲諸如C語言棧這種傳統的棧,用於支持像C / C + +這種非Java語言編寫的本地方法。本地內存棧同樣也可以在運行時擴展。如果沒有足夠的內存類擴展現有的本地內存棧或爲一個新的線程創建本地內存棧,你將會看到一個java.lang.OutOfMemoryError 。

You may have seen a java.lang.StackOverflowError, which is completely different from a java.lang.OutOfMemoryError. A java.lang.StackOverflowError is thrown when native memory stacks or Java Virtual Machine stacks need more memory than is configured. In most IBM Java Virtual Machine implementations, the -Xmso command-line option controls the stack size for operation system threads or native thread, and the -Xss command-line option controls the stack size for Java threads. In some implementations, such as Sun Microsystems HotSpot Java Virtual Machine, the Java methods share stack frames with C/C++ native code. The maximum stack size for a thread can be configured with the -Xss Java command-line option. The default sizes of these options vary by platform and implementation, but are usually between 256 Kbytes-1024 Kbytes. Please refer to the documentation of your Java virtual machine for more specific information. We will cover more about java.lang.StackOverflowError in a separate article.

        你也許看到過java.lang.StackOverflowError ,它是與java.lang.OutOfMemoryError完全不同的一種錯誤。當本地內存棧或Java虛擬機棧需要比配置更多的內存時,就會拋出一個java.lang.StackOverflowError。在大多數IBM的Java虛擬機的實現中, - Xmso設置的命令行選項控制運行系統線程或本地線程的堆棧大小,而-Xss命令行選項則控制對Java線程的棧大小。在一些實現中,如Sun公司的HotSpot Java虛擬機,Java方法與C / C + +的本機代碼共享棧幀。線程的最大堆棧大小可以使用-Xss Java命令行選項配置。這些選項的默認大小因平臺和JVM實現而異,但通常是256KB~1024KB之間。請參閱您的Java虛擬機的來查看更多的具體信息。我們將會在另一篇文章中詳細介紹更多關於java.lang.StackOverflowError的內容。

Now that we understand which memory areas could cause a java.lang.OutOfMemoryError, let's take a look at actual error messages. What does a java.lang.OutOfMemoryError look like and how can I address each symptom? Have you ever seen a java.lang.OutOfMemoryError similar to the following?

        現在我們已經瞭解了哪些內存區可能會引起 java.lang.OutOfMemoryError,下面我們來看看一些實際的錯誤消息。 java.lang.OutOfMemoryError到底是什麼樣的?我們如何去針對不同的錯誤症狀來去解決OOM問題。大家有沒有見過像類似下面這一行所展示的 java.lang.OutOfMemoryError?

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

This error message indicates that there is a memory request for an array but that's too large for a predefined limit of a virtual machine. What do we do if we encounter this kind of java.lang.OutOfMemoryError? We need to check the source code to make sure that there's no huge array created dynamically or statically. Fortunately, latest virtual machines usually do not have this limit.

        通過這個錯誤消息我們可以瞭解到,是由於我們要爲一個數組分配內存,但是這個請求的數組內存大小已經超過了JVM的預定義大小限制。那如果我們碰到這種場景的 java.lang.OutOfMemoryError該怎麼辦呢?如果遇到這種情況,我們就需要檢查下源代碼以確定程序沒有動態或者靜態的創建這樣的超大數組,幸運的是,在最新版的虛擬機中則沒有這個限制。

java.lang.OutOfMemoryError: PermGen space

You will see an OutOfMemoryError when the Permanent Generation area of the Java heap is full, like the above message.

        在Java堆的永久代區域內存被填滿時,你同樣也會看到像上面那樣的OOM錯誤信息。

On some Java Virtual Machines, such as Sun Microsystems' HotSpot Java Virtual Machine, a dedicated memory area called permanent generation (or permanent region) stores objects that describe classes and methods. We can visualize the usage of a permanent generation with the IBM Pattern Modeling and Analysis Tool for the Java Garbage Collector.

        在一些Java虛擬機中,例如,像Sun Microsystems(已被Oracle收購)的HotSpot虛擬機,會有一個稱之爲持久代(或持久區)的專用內存區域,用來存儲描述類和方法的對象,我們可以使用IBM的PMAT工具通過可視化的方式來查看持久代的情況。

In Figure 2 we enabled the "Max Perm" button and  the "Used Tenured" button to visualize permanent generation usage and its maximum size. We can see that the used amount of permanent generation reaches its maximum limit. That's why we're getting the java.lang.OutOfMemoryError: PermGen space message. If there's no memory leak, we can just use the -XX:MaxPermSize command-line option to increase the maximum limit of the permanent generation. For example,

-XX:MaxPermSize=128m

will set the maximum size of the permanent generation to 128 Mbytes.

        在圖2中,我們通過點擊"Max Perm"和 "Used Tenured"按鈕來可視化展示持久代的使用情況和持久代的最大大小。我們通過圖看到持久代的使用數量已經達到持久代的最大限制大小,這也就是爲什麼我們會得到一個“java.lang.OutOfMemoryError: PermGen space ”異常消息的原因。如果不存在內存泄露的話,我們可以使用通過調節 -XX:MaxPermSize 命令行選項來增加持久代的最大大小。例如,我們可以設置 -XX:MaxPermSize=128m 來將持久代大小設置爲128MB。

So far we've seen a Java.lang.OutOfMemoryError due to exhaustion in the Java heap or an area in the Java heap such as permanent generation. Surprisingly, a Java.lang.OutOfMemoryError can be thrown when the Java Virtual Machine cannot find any more memory in the native memory as well as in the Java heap. How can we tell whether it's caused by the Java heap or native memory?

        到目前爲止,我們已經看到Java堆內存耗盡或者Java堆的持久代內存耗盡而產生java.lang.OutOfMemoryError的情況。但是奇怪的是,當Java虛擬機在本地內存裏面無法分配更多的內存時,同樣會拋出java.lang.OutOfMemoryError錯誤,如同Java堆空間不足,同樣產生的java.lang.OutOfMemoryError錯誤一樣。那我們如何區分這個OOM是由於Java堆存儲空間耗盡產生的?還是本地內存空間不夠產生的呢?


In the following message, there's no information in the message whetherjava.lang.OutOfMemoryError is caused by the Java heap or native memory:

    在下面這個錯誤信息中,沒有足夠的信息可以判斷出這個OOM是由於Java堆產生的還是本地內存產生的:

JVMDUMP013I Processed dump event "systhrow",detail "java/lang/OutOfMemoryError".


In the following case, the Java virtual machine is kind enough to tell usthat there's native memory exhaustion. In the message, the Java virtual machinesays "allocateMemory failed" which means a native memory allocationfailed:

    在下面這種情況,Java虛擬機提供了足夠的信息,我們可以藉由此判斷出OOM是由於本地內存耗盡所導致的本地內存分配失敗。

java.lang.OutOfMemoryError: JVMCI046: allocateMemoryfailed


In the following message, there's no clue as to whether it's native memoryor a Java heap. Fortunately we have a line number, 20, and the source code filename, HeapExhaustionSimulator.java. This might be Java heap related.

    在下面這則簡短錯誤消息中,我們也沒有找到任何線索來說明OOM是由於本地內存或者Java堆內存所致。幸運的是,我們可以通過HeapExhaustionSimulator.java的第20行源代碼來查找,也許就跟Java堆有關。

JVMDG274: Dump Handler has Processed OutOfMemory.
Exception in thread "main" java.lang.OutOfMemoryError
at HeapExhaustionSimulator.main(HeapExhaustionSimulator.java:20)


In the following message, there's no clue whether it's native memory or aJava heap. But "sun.misc.Unsafe.allocateMemory(Native Method)"indicates that it might be native memory related.

    在下面這則簡短錯誤消息中,我們同樣也沒有找到任何線索來說明OOM是由於本地內存或者Java堆內存所致。但是,"sun.misc.Unsafe.allocateMemory(Native Method)" 這句也許說明了可能跟本地內存不足有關。

Exception in thread "main"java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:99)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)
at NativeMemorySimulator.main(NativeMemorySimulator.java:11)


In the following message, the  Java Virtual Machine indicates thatthe Java heap space is related to the java.lang.OutOfMemoryError.

    同樣,下面的錯誤消息說明OOM跟Java堆空間有關。

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid6280.hprof ...
Heap dump file created [50549348 bytes in 1.444 secs]


You may have seen a java.lang.OutOfMemoryError similar to the following:

    你可能也見過像下面這樣的OOM內容:

java.lang.OutOfMemoryError: requested NNN bytes for MMMM.Out of swap space?

Literally you could check the operating system configuration for swapspace. It seems that the Java Virtual Machine is not sure if the swap space isthe root cause of the problem (?).We can check whether this Java VirtualMachine is consuming too much native memory .We also need to make sure there'senough memory for this JVM and no other processes are consuming most of memoryresource. The last thing we can try is to find any known defects related to themodule, MMMM.

    根據上面錯誤消息的字面理解,你可以檢查下操作系統的Swap空間配置(僅Linux/Unix OS纔有)。這條消息看起來似乎是Java虛擬機並不確定Swap空間是產生OOM的根本原因。我們可以進一步檢查下java虛擬機是否使用了太多的本地內存。我們同樣也需要確認下我們是否爲Java虛擬機預留了足夠的內存,而且沒有其他進程使用了大部分內存資源。最後,我們可以嘗試查找任何與MMMM模塊有關的問題。


java.lang.OutOfMemoryError: unable to create new nativethread

This kind of message is seen when you have an excessive number of threadsor if the native memory is exhausted and a thread is attempting to be created.

    另外,當虛擬機已經有過多的線程或者在本地內存耗盡時再嘗試創建一個線程時,會拋出上面這種OOM錯誤。

 下一篇:java.lang.OutOfMemoryError (OOM)解密 & Java heap dumps 解析 (二)

原文鏈接地址:http://java.sys-con.com/node/1229281



發佈了31 篇原創文章 · 獲贊 24 · 訪問量 84萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章