heapdump - GANGNAM-JAVA/JAVA-STUDY GitHub Wiki

Memory Leak

ํ”„๋กœ๊ทธ๋žจ์ด OOM(Out Of Memory) ์˜ค๋ฅ˜๋กœ ์ข…๋ฃŒ๋˜๊ฑฐ๋‚˜ ์‹คํ–‰ ์†๋„๊ฐ€ ์ €ํ•˜๋˜๋Š” ํ˜„์ƒ.

์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” OOM 2๊ฐ€์ง€
1. java.lang.OutOfMemorryError: heap space
2. java.lang.OutOfMemorryError: GC Overhead limit exceed

์ถœ์ฒ˜์— ๋”ฐ๋ฅด๋ฉด, ์ž๋ฐ” ํ”„๋กœ์„ธ์Šค๊ฐ€ ์•ฝ 98%์˜ ์‹œ๊ฐ„์„ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ ์†Œ๋ชจํ•˜๊ณ 

์‹คํ–‰ ์ดํ›„์—๋„ 2% ์ด์ƒ์˜ ํž™ ๊ณต๊ฐ„์„ ๋‹ค์„ฏ ๋ฒˆ ์ด์ƒ ํ™•๋ณดํ•˜์ง€ ๋ชปํ•  ๋•Œ GC Overhead limit exceed ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

OOM์— ๋Œ€์ฒ˜ํ•˜๋Š” ๋ฐฉ๋ฒ•

ํ”„๋กœ๊ทธ๋žจ์ด OOM์œผ๋กœ ์ธํ•ด ์ข…๋ฃŒ๋  ๋•Œ ์ž๋™์œผ๋กœ ๋‹น์‹œ์˜ ๋ฉ”๋ชจ๋ฆฌ ์ •๋ณด๋ฅผ ํŒŒ์ผ(Heap Dump)๋กœ ๋‚จ๊ฒจ์•ผ ํ•œ๋‹ค.

-XX:+HeapDumpOnOutOfMemoryError

์œ„ ๋ช…๋ น์–ด๋ฅผ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์˜ต์…˜์— ์ถ”๊ฐ€ํ•˜๋ฉด OOM์ด ๋ฐœ์ƒํ•  ๋•Œ

JVM์„ ์‹œ์ž‘ํ•œ ๋””๋ ‰ํ† ๋ฆฌ์— Heap Dump ํŒŒ์ผ(.hprof)ํŒŒ์ผ์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค.

-XX:HeapDumpPath

์œ„ ๋ช…๋ น์–ด๋Š” Heap Dump ํŒŒ์ผ์„ ์ƒ์„ฑํ•  ์œ„์น˜๋ฅผ ์ง€์ •ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.


์ด ํŒŒ์ผ์ด ์—†๋‹ค๋ฉด ํ˜„์ƒ์„ ์žฌํ˜„ํ•œ ํ›„ ํž™๋คํ”„ ํŒŒ์ผ์„ ์ˆ˜๋™์œผ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.

๋จผ์ € Heap Dump ํŒŒ์ผ์„ ์ˆ˜๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ณ  ๊ทธ ํ›„์— ์ƒ์„ฑ๋œ Heap Dump ํŒŒ์ผ์„ ๋ถ„์„ํ•ด๋ณธ๋‹ค.

Heap Dump ํŒŒ์ผ ๋ถ„์„ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ํฌ๊ฒŒ 2๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  1. CLI

  2. ์ดํด๋ฆฝ์Šค MAT(Eclipse Memory Analyzer)

Heap Dump๋ž€?

Heap Dump : ํŠน์ • ์‹œ์ ์˜ Heap ์ƒํƒœ(์ •๋ณด)๋ฅผ ํŒŒ์ผ๋กœ ๋ณต์‚ฌํ•ด ์ €์žฅํ•˜๋Š” ์ž‘์—….

Memory Leak์˜ ์›์ธ์„ ๋ถ„์„ํ•  ๋•Œ ์ž์ฃผ ์‚ฌ์šฉ๋œ๋‹ค.

- Heap : ์ธ์Šคํ„ด์Šค๊ฐ€ ์ €์žฅ๋˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ
- Dump : ์ •๋ณด๋ฅผ ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ ๋ณต์‚ฌํ•ด ๊ฐ€์„œ ์ €์žฅํ•˜๋Š” ๊ฒƒ 

vi ๋“ฑ์˜ ํˆด์„ ์ด์šฉํ•ด์„œ Heap Dump ํŒŒ์ผ์„ ์—ด๋ฉด ์•ˆ๋œ๋‹ค.

์—ฌ๋Š” ์ˆœ์‚ฐ ํŒŒ์ผ์˜ ํž™ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์ด์ฆˆ๋งŒํผ ์„œ๋ฒ„์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.

๊ทธ๋Ÿฌ๋ฏ€๋กœ ๋กœ์ปฌ ์ปดํ“จํ„ฐ๋กœ ๋ณต์‚ฌํ•œ ํ›„ ํŒŒ์ผ์„ ์—ด์–ด์•ผ ํ•œ๋‹ค.

Heap Dump ํŒŒ์ผ ์ƒ์„ฑ ๋ฐ ๋ถ„์„(CLI)

OOM์ด ๋ฐœ์ƒํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

import java.util.HashMap;
import java.util.Map;

public class HeapDumpTest {
    private Map<Integer, MemoryObject> leak = new HashMap<>();

    public static void main(String[] args) {
        HeapDumpTest heapDumpTest = new HeapDumpTest();
        heapDumpTest.run();
    }

    private void run() {
        for(int i = 0; ; i++) {
            // ํ•ด์‹œ๋งต์— ์ €์žฅ๋งŒ ํ•˜๊ณ  ๋นผ๋‚ด์ง€ ์•Š์•„ ๊ฒฐ๊ตญ OOM์ด ๋‚˜๋Š” ๊ตฌ์กฐ.
            leak.put(i, new MemoryObject(i));
            System.out.println("leaking object " + leak.get(i).index);
            if( i == 5000 ) {
                try {
                    // ๋ฐ๋ชจ๋ฅผ ์œ„ํ•ด 5000๋ฒˆ ์ดํ›„ sleep.
                    System.out.println("Sleeping after adding " + i + "th element.");
                    Thread.sleep(100000000L);
                } catch (final Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class MemoryObject {
        int index ;
        MemoryObject(final int index) {
            this.index = index;
        }
    }
}

5001๋ฒˆ์งธ MemoryObject๊ฐ€ leak์— ์ €์žฅ๋˜๋ฉด sleep ๋œ๋‹ค.

leaking object 4996
leaking object 4997
leaking object 4998
leaking object 4999
leaking object 5000
Sleeping after adding 5000 th element.

Heap Dump ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด JDK ๋‚ด์˜ jps ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด HeapDumpTest์˜ ํ”„๋กœ์„ธ์Šค ๋ฒˆํ˜ธ(PID)๋ฅผ ์ฐพ๋Š”๋‹ค.

$ jps
5408 Launcher
7744 KotlinCompileDaemon
14436 Jps
10460 HeapDumpTest
2348

๊ทธ๋ฆฌ๊ณ  JDK ๋‚ด์˜ jmap ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ Heap Dump ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์ž

$ jmap -dump:live,file=./heapdump.hprof 10460
Dumping heap to C:\study\JAVA-STUDY\heapdump.bin ...
Heap dump file created
  • dump:live : ๋™์ž‘์ค‘์ธ ์ธ์Šคํ„ด์Šค๋งŒ ์บก์ณ

  • file : Dump ํŒŒ์ผ ์ €์žฅ ๊ฒฝ๋กœ

DumpํŒŒ์ผ์„ ํ™•์ธํ•œ ํ›„, jhat ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ํŒŒ์ผ์„ ์‹คํ–‰์‹œํ‚จ๋‹ค.

$ jhat -J-Xmx6g -port 7000 ./heapdump.hprof
Reading from ./heapdump.hprof...
Dump file created Tue Jul 07 07:32:16 KST 2020
Snapshot read, resolving...
Resolving 30042 objects...
Chasing references, expect 6 dots......
Eliminating duplicate references......
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
  • Xmx : ์‹คํ–‰์‹œํ‚ฌ ํ”„๋กœ๊ทธ๋žจ์˜ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์ด์ฆˆ ์„ค์ •

  • 7000 : Dump ํŒŒ์ผ์„ ์‹คํ–‰์‹œํ‚ฌ ํฌํŠธ ์ด์ œ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ํ†ตํ•ด https://localhost:7000 ์ฃผ์†Œ๋กœ ์ ‘์†ํ•ด๋ณด์ž

์ด ํ™”๋ฉด์—์„œ ๋ชจ๋“  ํŒจํ‚ค์ง€๋“ค๊ณผ ํด๋ž˜์Šค๋“ค์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ์œ ์ถœ์„ ์ฐพ๊ธฐ ์œ„ํ•ด "Show heap histogram" ๋งํฌ๋ฅผ ํด๋ฆญํ•˜์ž.

์ด ํ‘œ๋Š” Total Size ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌ๋˜์–ด์žˆ๋‹ค.

๊ฐ„๋‹จํ•œ ํ”„๋กœ๊ทธ๋žจ ํ•˜๋‚˜๋ฅผ ๋Œ๋ ธ์„ ๋ฟ์ธ๋ฐ ์ˆ˜๋งŽ์€ ์ธ์Šคํ„ด์Šค๋“ค์ด ์ƒ์„ฑ๋˜์—ˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์œ„์—์„œ 7๋ฒˆ์งธ์— ์šฐ๋ฆฌ๊ฐ€ ์ฐพ๊ณ ์ž ํ•˜๋Š” ํด๋ž˜์Šค ์ด๋ฆ„์ด ๋ณด์ธ๋‹ค.

ํ™•์‹คํžˆ 5001๊ฐœ๊นŒ์ง€๋งŒ ์ƒ์„ฑ๋˜์—ˆ๋‹ค. MemoryObject๋ฅผ ํด๋ฆญํ•˜์ž.

References to this object์— 5001๊ฐœ์˜ MemoryObject ์ธ์Šคํ„ด์Šค๋“ค ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋‚˜์˜จ๋‹ค. ์•„๋ฌด๊ฑฐ๋‚˜ ํ•œ๊ฐœ ํด๋ฆญํ•ด๋ณด์ž.

์ด์ œ ์–ด๋Š ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ์ด ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ์ฐพ๊ธฐ ์œ„ํ•ด Exclude Weak refs๋ฅผ ํด๋ฆญํ•œ๋‹ค.

์ด ํ™”๋ฉด์„ ํ†ตํ•ด ์ด ์ธ์Šคํ„ด์Šค๊ฐ€ ์–ด๋””์— ์†ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์ด ์ธ์Šคํ„ด์Šค๋Š” HeapDumpTest ํด๋ž˜์Šค์˜ leak ์ด๋ผ๋Š” ํ•„๋“œ์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค.

๋˜ํ•œ leak ํ•„๋“œ๋Š” HashMap์˜ ์ธ์Šคํ„ด์Šค์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด MemoryObject๋Š” HashMap ์ธ์Šคํ„ด์Šค์˜ table ํ•„๋“œ์— ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค.

๊ฒฐ๊ตญ leak์ด๋ผ๋Š” HashMap ์ธ์Šคํ„ด์Šค๊ฐ€ MemoryObject ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  leak์ด Memory Leak์˜ ์›์ธ์ž„์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

Heap Dump ํŒŒ์ผ ์ƒ์„ฑ ๋ฐ ๋ถ„์„(์ดํด๋ฆฝ์Šค MAT)

์ด ๋งํฌ์—์„œ ์ดํด๋ฆฝ์Šค MAT์„ ๋‹ค์šด๋ฐ›๋Š” ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ƒ์„ฑ๋œ Heap Dump ํŒŒ์ผ์„ MAT์œผ๋กœ ์‹คํ–‰์‹œํ‚ค๋ฉด ์œ„์™€ ๊ฐ™์€ ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚œ๋‹ค.

๋จผ์ € ๋ช‡ ๊ฐ€์ง€ ์šฉ์–ด๋ฅผ ์ •์˜ํ•˜์ž.

  • with incomming references : ํ•ด๋‹น Object๋ฅผ ์ฐธ์กฐํ•˜๋Š” Object ๋ณด์—ฌ์คŒ
  • with outcomming references : ํ•ด๋‹น Object๊ฐ€ ์ฐธ์กฐํ•˜๋Š” Object ๋ณด์—ฌ์คŒ
  • Shallow Heap : ํ•˜๋‚˜์˜ ๊ฐ์ฒด๊ฐ€ ์ฐจ์ง€ํ•˜๋Š” ์˜์—ญ
  • Retained Heap : ํ•ด๋‹น ๊ฐ์ฒด์˜ ๋ฉ”๋ชจ๋ฆฌ์™€ ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ ์ฐธ์กฐํ•˜๋Š” ๊ฐ์ฒด๋“ค์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํฌํ•จํ•œ ์˜์—ญ. Memory Leak์„ ๋ถ„์„ํ•  ๋•Œ ํŠนํžˆ ์‹ ๊ฒฝ์จ์•ผ ํ•  ์˜์—ญ

Details

ํž™์— ๋Œ€ํ•œ ๊ฐœ๋žต์ ์ธ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

Unreachable Objects Histogram : ํด๋ž˜์Šค๋ณ„๋กœ ์ƒ์„ฑ๋œ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

Biggest Objects by Retained Size

ํž™ ์˜์—ญ์— ์ƒ์„ฑ๋œ Objects๋“ค ์ค‘, ๊ฐ€์žฅ ๋งŽ์€ ์˜์—ญ์„ ์ฐจ์ง€ํ•˜๋Š” ์ฃผ์š” Object์˜ ๋น„์œจ์„ ํŒŒ์ด์ฐจํŠธ๋กœ ํ‘œ์‹œํ•œ๋‹ค.

์šฐ์ธก์˜ ์ง„ํ•œ ํŒŒ๋ž€์ƒ‰ ์˜์—ญ์„ ์šฐํด๋ฆญํ•œ ํ›„ List Objects -> with outgoing references๋ฅผ ํด๋ฆญํ•˜๋ฉด

์ฝ”๋“œ์—์„œ ์ƒ์„ฑํ–ˆ๋˜ HashMap ๋…ธ๋“œ๊ฐ€ ์•ฝ 5,000๊ฐœ ์ƒ์„ฑ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

Actions

์ƒ์„ฑ๋œ Object๋“ค์— ๋Œ€ํ•œ ์„ธ๋ถ€ ๋‚ด์—ญ์„ ๋ณด์—ฌ์ค€๋‹ค.

Histogram : ํž™ ์˜์—ญ์— ์ƒ์„ฑ๋œ ๊ฐ์ฒด ํƒ€์ž…์— ๋”ฐ๋ฅธ ๊ฐ์ฒด์˜ ์ˆ˜๋ฅผ ํ‘œ์‹œ.

Dominator Tree : ์ƒ์„ฑ๋œ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ธ๊ณผ๊ด€๊ณ„(์ƒ์„ฑ๊ด€๊ณ„)๋ฅผ ๊ณ„์ธต ํ˜•ํƒœ๋กœ ํ‘œ์‹œํ•˜๋ฉฐ ์ƒ์„ฑ๋œ ๊ฐ์ฒด์˜ ํฌ๊ธฐ ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ์ ์œ  %๋ฅผ ํ‘œ์‹œํ•œ๋‹ค.

์•„๋ž˜ Reports์˜ LeakSuspect๋ฅผ ํ†ตํ•ด ํ˜„ ๋ฉ”๋ชจ๋ฆฌ ์ƒํ™ฉ์— ๋Œ€ํ•œ ํžŒํŠธ๋ฅผ ์–ป์€ ํ›„, Dominator Tree์—์„œ ์ž์„ธํ•˜๊ฒŒ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์ด ํšจ์œจ์ ์ด๋‹ค.

์ฝ”๋“œ์—์„œ ์ƒ์„ฑํ–ˆ๋˜ HashMap ๋…ธ๋“œ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Top Consumers : ๊ฐ€์žฅ ๋งŽ์ด ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์„ธ๋ณด ์ •๋ณด ํ‘œ์‹œํ•œ๋‹ค.

Duplicate Classes : ์ค‘๋ณต ์ƒ์„ฑ๋œ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•œ๋‹ค.

Reports

ํž™ ์˜์—ญ์— ๋Œ€ํ•œ ๋ถ„์„ ๋ฆฌํฌํŠธ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

LeakSuspect : ๋ฉ”๋ชจ๋ฆฌ ๋ฆญ(Leak)์œผ๋กœ ์˜์‹ฌ๋˜๋Š” ๊ฐ์ฒด์— ๋Œ€ํ•œ ๋ถ„์„์„ ๋ณด์—ฌ์ค€๋‹ค.

์ฝ”๋“œ์—์„œ ์ƒ์„ฑํ–ˆ๋˜ HashMap ๋…ธ๋“œ๊ฐ€ Problem Suspect 1๋กœ ์„ ์ •๋˜์–ด ์ƒ๋‹จ์— ๋“ฑ์žฅํ•œ๋‹ค.

Top Conponents : ํž™ ์˜์—ญ์—์„œ ๊ฐ€์žฅ ๋งŽ์€ ์˜์—ญ์„ ์ฐจ์ง€ํ•˜๋Š” ๊ฐ์ฒด์— ๋Œ€ํ•œ ์„ธ๋ถ€์ ์ด๊ณ  ์ข…ํ•ฉ์ ์ธ ๋ถ„์„์„ ๋ณด์—ฌ์ค€๋‹ค.

Step By Step

MAT์„ ์ด์šฉํ•˜์—ฌ Heap Dump๋ฅผ ๋ถ„์„ํ•˜๋Š” ์ ˆ์ฐจ ๋ฐ๋ชจ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.