Heap Memory

 

프로그램이 구동되기 위해서는, 먼저 메모리에 올라가야한다.

메모리는 크게 4가지 ( Code, Data, Heap, Stack ) 으로 나뉘어있다.

 

그중에서 동적으로 할당되고 해제되는 영역인, Heap Memory 관련해서 작성하려한다.

 

c에서는 malloc(), free() 등으로 할당과 해제를 반복하며,

java에서는 GC ( Garbage Collector ) 를 통해 자동으로 정리해준다.

 

2021.04.04 - [서버운영/자바 GC ( Garbage Collection )] - GC ( Garbage Collection )

 

GC ( Garbage Collection )

GC , 가비지 컬렉션 이란? 자바 프로세스가 동적으로 할당했던 메모리 영역중에, 필요없는 영역을 해제하는 기능 => GC가 없다면, 더이상 사용하지 않는 잉여객체들이 메모리를 모두 차지해  OOM (

st-it.tistory.com


Heap Dump

 

Heap Memory는 프로세스가 동작할때 최소/최대 메모리를 지정하며, 할당된 메모리 안에서 동적으로 운영된다.

 

그리고 할당된 메모리가 부족한 경우, OOM ( Out Of memory )가 발생하며 프로그램이 종료된다.

1) 메모리 누수로 인해 정리가 되지않아, 최대 메모리 사용량에 도달했을 때
2) 현재 남아있는 메모리보다 큰 리퀘스트가 들어왔을 때

 

OOM이 발생하면 그때의 Heap Memory 상태 스냅샷을 확보할수 있으며,

.hprof 확장자의 heap dump 파일로 저장된다.

-XX:-HeapDumpOnOutOfMemoryError : OOM 발생시 Heap dump 생성
-XX:HeapDumpPath                         : Heap dump 파일의 위치

 

OOM이 없더라도, 메모리 누수가 의심되거나 현재의 메모리 상태를 확인하고 싶을 때

jmap을 사용하여, 수동으로 Heap memory 상태를 dump 파일로 저장할 수도 있다.

jmap -dump:format=b,file=$PATH/$file.hprof $PID

Heap dump 분석

 

주로 Eclipse MAT ( Memory Analyzer Tool )을 사용한다.

 

사용전에 설치폴더 MemoryAnalyzer.ini 설정파일을 열어, 분석툴의 메모리를 정해주어야한다.

기본 1GB로 되어있지만, Heap dump 파일이 그보다 큰 경우 프로그램이 종료되기 때문이다.

 

이후에 파일을 넣어주면 아래와 같은 화면이 나온다.

문제로 예상되는 17번 쓰레드가, 전체 1.5GB 중에 94.54%를 차지하고 있었다.

 

웹로직 기반의 쓰레드이며, MAT는 char[]를 로드하려다가 문제가 발생한것으로 예상했다.

 

 

오브젝트 리스트를 살펴보면, MAT 예상대로 char 배열이 1.5GB 대부분을 차지중이었다.

 

 

해당 Thread dump 를 분석해보았다.

[ACTIVE] ExecuteThread: '17' for queue: 'weblogic.kernel.Default (self-tuning)'
  at java.lang.OutOfMemoryError.<init>()V (OutOfMemoryError.java:48)
  at java.util.Arrays.copyOf([CI)[C (Arrays.java:3332)
  at java.lang.AbstractStringBuilder.ensureCapacityInternal(I)V (AbstractStringBuilder.java:124)
  at java.lang.AbstractStringBuilder.append(Ljava/lang/String;)Ljava/lang/AbstractStringBuilder; (AbstractStringBuilder.java:448)
  at java.lang.StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer; (StringBuffer.java:270)
  at com.cspi.cornerstone.container.TemplateParser.transferObjectTag(Ljava/lang/StringBuffer;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/StringBuffer; (TemplateParser.java:368)
  at com.cspi.cornerstone.j2ee.http.HttpResponse.out(Lcom/cspi/cornerstone/cmps/Composer;)V (HttpResponse.java:130)
  at jsp_servlet._contents._voc._global_voc._process._detailview.__voc_detailview.run(Lcom/cspi/cornerstone/j2ee/http/HttpRequest;Lcom/cspi/cornerstone/j2ee/http/HttpResponse;)V (__voc_detailview.java:2481)
  at jsp_servlet._contents._voc._global_voc._process._detailview.__voc_detailview._jspService(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V (__voc_detailview.java:2727)
.
.
.
.

 

대략 분석해보면

 

1) OOM이 발생했고

2) Array, String Builder 즉 위의 Char[] =1.5GB 짜리를 String 으로 변환하려다 발생한 OOM?

   혹은 추가로 Char 배열이 들어오려다, 메모리가 부족해서 OOM이 발생한것으로 보인다

3) voc 관련한 소스코드쪽에서 유발된 것 같다

 

 

실제 분석결과, VOC 본문 ( content ) 작성 시

사용자가 프로그램 예약어를 사용해서 무한루프가 돌면서 내용 생성되는 문제가 있었다.

 

물론 Dump 분석으로는 정확한 유발지 및 원인을 찾기는 어렵지만,

이처럼 대략적인 방향을 잡을수있다.

 

'서버운영 > 자바 프로세스 관리' 카테고리의 다른 글

Thread dump  (0) 2021.07.15

Thread란

프로세스 내에서 실행되는 흐름의 단위
※ 프로세스 : 특정 서비스를 위해, OS내에서 메모리를 할당받아 수행되는 프로그램

하나의 프로세스에 여러개의 쓰레드가 존재할 수 있으며, 각 쓰레드는 아래의 상태값을 가진다.

 

① NEW : 스레드가 생성되었지만 스레드가 아직 실행할 준비가 되지 않았음
② RUNNABLE : 스레드가 실행되고 있거나 실행준비되어 스케쥴링은 기달리는 상태
③ WAITING : 다른 스레드가 notify(), notifyAll()을 불러주기 기다리고 있는 상태(동기화)
④ TIMED_WAITING : 스레드가 주어진 시간동안, 대기하고 있는 상태
⑤ BLOCK : 사용하고자하는 객체의 Lock이 풀릴때까지 대기하는 상태
⑥ TERMINATED : 스레드가 종료한 상태

 

JVM에 의해 관리되며, 설명해놓은 그림이 있어 가져와봤다.

 

[출처] https://raccoonjy.tistory.com/15


Thread dump

프로세스에 수행중인 모든 쓰레드들의 상태를 스냅샷 수행

보통 아래 상황일때 Thread 분석을 한다.

 

1) 어플리케이션이 Hang 상태일 때

2) 서비스 처리가 늦어질 때

3) CPU 사용량이 높을 때

 

Thread Lock이 원인일수도, 여유 Thread가 없어서 일수도 있다.

SQL 지연일수도, 특정 코드 Stack 에서 막힌것일수도 있어 분석툴로 확인해야한다.

 

하지만 하나의 스냅샷만으로는 정확한 원인파악이 어렵다.

여러개의 스냅샷을 비교하며, 쓰레드의 변화상태를 통해야만 특정 쓰레드를 원인지을수 있다.

 

빠른시간내에 여러개 덤프를 떠서, 순간의 변화사항 분석이 필요하다

( Ex: 5초내 여러개 Thread dump 획득 )

 

 

생성방법

 

1) kill -3 $PID

 

 

2) jstack 활용

 

jps-v 로 PID를 확인 가능하며, ps -ef | grep 을 통해서도 특정 PID를 알 수 있다.

jstack $PID > log.txt

분석

 

일반적으로 쓰레드 덤프 구조는 아래와 같다

"pool-1-thread-13" prio=6 tid=0x000000000729a000 nid=0x2fb4 runnable [0x0000000007f0f000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.read(SocketInputStream.java:129)at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)- locked <0x0000000780b7e688> (a java.io.InputStreamReader)at java.io.InputStreamReader.read(InputStreamReader.java:167)at java.io.BufferedReader.fill(BufferedReader.java:136)at java.io.BufferedReader.readLine(BufferedReader.java:299)- locked <0x0000000780b7e688> (a java.io.InputStreamReader)at java.io.BufferedReader.readLine(BufferedReader.java:362) 

쓰레드 이름: Thread-(Number) 혹은 pool-(number)-thread-(number)로 지정된 고유이름

우선순위 : 쓰레드의 우선순위

쓰레드 ID

쓰레드 상태

쓰레드 콜스택

 

 

저런 로그들이 쓰레드마다 존재하기에, 육안으로 구별하기는 힘들다.

보통 분석툴을 사용하여 변화사항을 비교한다.

 

 

 

분석툴

 

1) Ibm thread and monitor dump analyzer for java

 

2) fastThread ( https://fastthread.io/ )

 

 

 

분석예시

 

※ 해당부분은 타 블로그에서 분석한 내용을 가져와 서술하였습니다. - CPU 사례

( https://d2.naver.com/helloworld/10963 )

먼저, CPU를 가장 많이 점유하는 스레드가 무엇인지 추출한다.

추출한 결과에서 CPU를 가장 많이 사용하는 LWP(light weight process)와 고유 번호를 확인한다.


[user@linux ~]$ ps -mo pid,lwp,stime,time,cpu -C java
 
PID LWP STIME TIME %CPU
10029 - Dec07 00:02:02 99.5
10039 Dec07 00:00:00 0.1
- 10040 Dec07 00:00:00 95.5


파악한 PID를 통해 thread dump를 획득한다.

"NioProcessor-2" prio=10 tid=0x0a8d2800 nid=0x2737 runnable [0x49aa5000]
java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:210)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:65)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69)
- locked <0x74c52678> (a sun.nio.ch.Util$1)
- locked <0x74c52668> (a java.util.Collections$UnmodifiableSet)
- locked <0x74c501b0> (a sun.nio.ch.EPollSelectorImpl)


수초내 지속적으로 dump를 획득하여, 해당 Stack 별 변화를 비교해보며 원인을 찾는다.

 

 

 

'서버운영 > 자바 프로세스 관리' 카테고리의 다른 글

Heap dump  (0) 2021.07.16

+ Recent posts