2012년 6월 6일 수요일

안드로이드 프로세스 수명 주기


1. 수명 주기

메모리를 자동으로 할당해주고 자동으로 비워준다는 것은 가상머신을 사용하는 우리들에게 있어 약일 수도 있지만 오히려 반대로 따지고 보면 당장 반환해야 할 순간에 손만 빨고 있을 지도 모른다는 소리다. 하지만 여지껏 많은 개발자들은 그런 환경에서 완성도 높은 애플리케이션을 개발해 왔으니 이러한 주기를 잘 파악해서 프로그램을 작성해야 한다.

안드로이드 애플리케이션은 수명 주기를 제어하는 것을 제한 하고 있다. 

빠른 반응 처리를 위해 달빅 가상 머신은 애플리케이션을 바로 죽이지 않고 가지고 있는다. 종료한 애플리케이션임에도 불구하고 사용자가 다시 누를수 있다는 가정을 하고 있는 것이다. 

안드로이드는 우선 순위가 높은 것 부터 낮은 순으로 관리해서 높은 순위에 있는 애플리케이션에게 메모리를 우선적으로 할당해준다. 제한된 메모리 관리를 효율적으로 운용하기 위해 낮은 순위에 있는 애플리케이션은 우선 순위가 높은 애플리케이션이 더 많은 메모리가 필요할 때 종료되고 메모리를 반환하게 되어있다.


2. 프로세스 상태

안드로이드 애플리케이션은 단일 프로세스로서 하나의 달빅 인스턴스 위에서 베타적으로 실행된다. 

2-1. 활성 프로세스(Active processes)
중요한 우선 순위, foreground 상태로 현재 실행중인 애플리케이션을 뜻한다. 
2-2. 화면에 보이는 프로세스(Visible processes)
높은 우선 순위, 화면에 보이고는 있으나 비활성화된 프로세스들을 뜻한다.
2-3. 시작된 서비스 프로세스(Stated Service processes)
높은 우선 순위, 화면에 보이는 인터페이스 없이도 계속 되어야 하는 지속적인 처리를 지원

2-4. 백그라운드 프로세스(Background processes)
낮운 우선 순위, 화면에 보이는 액티비티를 가지고 있지 않으면서, 동시에 실행중인 서비스를 가지고 있지 않는 프로세스

2-5. 빈 프로세스(Empty processes)
낮은 우선 순위, 안드로이드 애플리케이션이 다시 띄워질 때 구동 시간을 향상시키기 위하여 이 캐시를 유지.


3. 애플리케이션 수명 주기 이벤트 

□ onCreate : 애플리케이션 생성시 호출, 모든 상태 변수와 공유 리소스를 초기화 

□ onTerminate : 애플리케이션 객체가 종료될 때 호출(항상 호출 된다는 보장을 할 수 없음)
리소스 회수를 위한 목적으로 커널에 의해 종료되는 경우 이 함수는 호출되지 않는다.

□ onLowMemory : 시스템 리소스가 부족할 때 애플리케이션이 추가로 메모리를 해제하는 기회를 준다. 모든 백그라운드 프로세스가 종료되었는데도 메모리가 부족하면 호출됨

□ onConfigurationChanged : 애플리케이션 차원에서 구성 변경을 다룰 필요가 있을 때

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import android.app.Application;
import android.content.res.Configuration;
 
public class MyApplication extends Application{
     
    private static MyApplication singleton;
     
    public static MyApplication getInstance() {
        return singleton;
    }
     
    @Override
    public final void onCreate() {
        super.onCreate();
        singleton = this;
    }
     
    @Override
    public final void onTerminate() {
        super.onTerminate();
    }
     
    @Override
    public final void onLowMemory() {
        super.onLowMemory();
    }
     
    @Override
    public final void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }
}

java.lang.OutOfMemoryError 2005년 분석결과


java.lang.OutOfMemoryError
      


이 문서는 기술지원 또는 개발 시 java.lang.OutOfMemoryError 를 만났을 때 원인 파악 및 해결방안 입니다.


java.lang.OutOfMemoryError 에는 크게 2가지 패턴이 있다고 볼 수 있습니다.(전적으로 개인적인 생각이지만..^^)
Java heap 메모리가 정말로 Full 되서 나는 종류가 있고 그렇지 않은데도 나는 종류가 있습니다.
그 둘 중에 어는 것 인지부터 가려내는 것이 가장 먼저 선행되어야 합니다.
Java 가상머신에는 메모리 구조가 여러단계로 나뉘어 져 있으므로 어느 영역에 의해 java.lang.OutOfMemoryError 가 나는지 알기 위해 JAVA OPTION으로 싸이트가 안정화 되기 전까진 verbosegc 또는 -XX:+PrintGCDetails  옵션을 추가해 놓는 것이 java.lang.OutOfMemoryError 났을 때를 대비해 좋은 습관이라 할 수 있습니다.

-verbosegc 또는  -XX:+PrintGCDetails  옵션으로 로그에 남는 heap 메모리 정보를 본 후 java.lang.OutOfMemoryError 가 날 때 heap이 꽉 차서 나는 것인지 아닌지 부터 판단합니다.

1.   Heap Memory Full 차지 않았을 때
1)   Perm 영역이 full 되는 경우

Permanent generation is full...
increase MaxPermSize (current capacity is set to: 134217728 bytes)

[Full GC[Unloading class sun.reflect.GeneratedSerializationConstructorAccessor31282]
 810207K->802132K(1013632K), 8.3480617 secs]
<GC: 2 4  2465625.831280 10348 0 31 113802808 105534632 286326784 0 0 35782656 715849728 715848840 715849728 134217720 134023296 134217728 8.348677 8.348677 >
Passwd Check =============
<2005. 5. 19. 오전 9 32 23 KST> <Error> <HTTP> <BEA-101017> <[ServletContext(id=2536415,name=/,context-path=)] Root cause of ServletException.
java.lang.OutOfMemoryError
위와 같은 case 인 경우네는 Java 가상머신중에 Perm 영역이 full 차는 경우 입니다
Perm 영역에는 class object 및 관련된 meta data가 로드되는 곳인데 싸이트가 매우 많은 수의 class를 사용하는 경우 늘려줘야 하는 case도 있습니다.
얼마가 적정한 사이즈란 정답이 없는 것 같고 max가 계속 늘어나지 않고 일정한 사이즈를 유지 하면 됩니다.

             위 에러가 났을때는 -XX:MaxPermSize=256m  옵션으로 사이즈를 적당하게 늘려주는게 방
             법이며 늘려주었는데도 불구하고 Full 차는 시간만 늘어날 뿐 계속 사이즈가 늘어난다면 이 영역은 일반 비즈니스 프로그램으로 핸들링 할 수 없는 영역이므로 WAS 제품의 버그 및 jdk 버그로 보는 것이 일반적입니다.
       
2)   MAXDSIZ 사이즈 관련
  - Exception in thread "CompileThread0" java.lang.OutOfMemoryError: requested 32756 bytes for ChunkPool::allocate
Possible causes:
         - not enough swap space left, or
         - kernel parameter MAXDSIZ is very small.
-        java.lang.OutOfMemoryError: unable to create new native thread
 
위 두 에러가 나는 이유는 Data 사이즈가 Full 차서 더 이상 메모리 할당을 할 수 없다는 java.lang.OutOfMemoryError 입니다.

Jdk도 내부적으로 c 라이브러리를 쓰고 jni를 통해서 c프로그램을 호출할 수 도 있는 환경에서 Data 영역이상으로 메모리 사용 시 위 에러를 만날 수 있습니다.
Heap 영역에는 java heap C heap이 있는데 C heap Data 메모리 영역에 영향을 미치는 것으로 보이며 보통 C의 전역 변수들이 잡히는 영역입니다.

위 현상을 만났을 때는 hp os MAXDSIZ가 너무 작게 잡혀있지 않은지 확인 후 적당한 크기로 늘려 줘야 합니다.

Glance 에서 shift+m 을 누른 후 jvm pid를 누르면 java가 사용하는 Data 사이즈를 모니터링 할 수 있습니다.
모니터링을 통해 적정한 사이즈로 MAXDSIZ를 늘려주어야 하며 만일 늘려 준게 에러 발생의 시간만 지연 시킬 뿐 계속 사용량이 늘어난다면 이는 사용하는 c라이브러리 쪽에 메모리 릭 버그가 있는 것이므로 c라이브러리를 체크 하셔야 합니다.
java.lang.OutOfMemoryError: unable to create new native thread
이 경우는 프로그램에서 Thread pool 프로그램 실수로 필요이상으로 쓰레드가 생성되는 경우도 과도하게 메모리를 사용할 수 있으므로 jvm 쓰레드 덤프를 통해 과도한 쓰레드가 생성되지 않았는지도 확인해 보셔야 합니다.

2.   Heap Full 찾을 때
이 경우는 java가 사용하는 heap 영역이 Full 되서 java.lang.OutOfMemoryError 가 나는 경우 인데 두 가지 패턴이 있습니다.
순간적으로 대량의 데이터를 메모리에 올리는 프로그램이 실행되어 문제를 야기 할수 있으며 다른 한 가지는 조금씩 메모리 릭이 발생하여 점차적으로 메모리가 쌓여 가는 경우 입니다.
두 가지 중에  어느 것인지 구별하는 것이 중요하며 이를 위해서도 마찬가지로 -verbosegc 또는  -XX:+PrintGCDetails  로그를 통해 순간적으로 메모리가 차는 것인지 조금씩 메모리가 차는 것인지를 확인하셔야 합니다.

1) 특정 프로그램의 특정시점의 과도한 메모리 사용에 의한 경우
                특정 프로그램이 과도하게 heap 메모리 이상 메모리를 사용하면서
java.lang.OutOfMemoryError가 발생하는 경우는 이 에러가 발생하는 시점의 쓰레드 덤프를 통해 어느 프로그램인지 쉽게 찾을 수 있습니다.
쓰레드 덤프에 메모리를 쓸만한 프로그램은 어느 정도의 자바프로그램 경험이 있으면 찾을 수 있습니다.

Ex)
"ExecuteThread: '36' for queue: 'default'" daemon prio=10 tid=0x0048e7b0 nid=48 lwp_id=4139729 runnable [0x23f32000..0x23f30500]
          at java.net.SocketInputStream.socketRead(Native Method)
          at java.net.SocketInputStream.read(Unknown Source)
          at oracle.net.ns.Packet.receive(Unknown Source)
          at oracle.net.ns.NetInputStream.getNextPacket(Unknown Source)
          at oracle.net.ns.NetInputStream.read(Unknown Source)
          at oracle.net.ns.NetInputStream.read(Unknown Source)
          at oracle.net.ns.NetInputStream.read(Unknown Source)
          at oracle.jdbc.ttc7.MAREngine.unmarshalUB1(MAREngine.java:718)
          at oracle.jdbc.ttc7.MAREngine.unmarshalSB1(MAREngine.java:690)
          at oracle.jdbc.ttc7.Oall7.receive(Oall7.java:373)
          at oracle.jdbc.ttc7.TTC7Protocol.doOall7(TTC7Protocol.java:1405)
          at oracle.jdbc.ttc7.TTC7Protocol.fetch(TTC7Protocol.java:889)
          - locked <0x35e8e3b0> (a oracle.jdbc.ttc7.TTC7Protocol)
          at oracle.jdbc.driver.OracleResultSetImpl.next(OracleResultSetImpl.java:242)
          - locked <0x36f66c98> (a oracle.jdbc.driver.OracleResultSetImpl)
          at weblogic.jdbc.pool.ResultSet.next(ResultSet.java:180)
          at Dcard.AAA.ejb.monitor.member.wbbb.WACBean.getInfoList(WACBean.java:5789)

java.lang.OutOfMemoryError 가 날 상황에 쓰레드 덤프를 두 세번 떠서 같은 쓰레드 번호로 위 같은 로직이 오래 결려있다면 이는 프로그램에서 while(rs.next()) 를 오랜 기간 돌면서 메모리에 DB로부터 읽어서 올리고 있다는 것을 의미 합니다 대량 데이터 조회가 일어나는 경우인 것 입니다.

이런 식으로 특정 프로그램의 특정 시점에 과도한 메모리 사용으로 인한 java.lang.OutOfMemoryError 에러는 쓰레드 덤프를 통해 찾을 수 있습니다.
-- 물론 2번에서 설명 할 heap dump를 통해서도 찾을 수 있습니다.

2) 메모리 릭에 의해 조금씩 메모리가 쌓인 경우
JVM위 에서 실행중인 프로그램중에 어떤 것이 GC 대상에서 제외되는 영역에 data를 조금씩 쌓고 있다는 얘기입니다.
GC 대상에서 제외되는 영역은 다음과 같은 곳이 있습니다.
- Http Session에 넣는 데이터..(세션 타임아웃 또는 invilidate 씨 까지 제외됨)
- Static 변수
- 서블릿 또는 jsp의 멤버변수 ( WAS 기동 후 최초 호출 시 인스턴스 화 되어 WAS가 내려 갈 때 까지 사라지지 않음 )
- EJB의 멤버변수( pool안에서 객체가 존재하는 한 GC대상에서 제외)
          위 같은 영역에 프로그램에서 data add 하는 구조를 메모리 릭 이라고 할 수 있습니다.
           IBM 또는 SUN JDK 인 경우에는 heapdump를 통해서 분석하여 어느 데이터가 메모리를 많이 잡고 있는지를 알 수 있습니다.
           Heapdump 사용법 및 분석 법은 다음을 참조 하시면 됩니다.


출처 :  2005-05-31 송학렬 ㈜아이티플러스 기술지원부

JVM MEMORY 영역에 대해



- Method Area : 메소드와 클래스 변수를 저장하기 위한 공간, 모든 프로그램에 의해 공유.
- Heap Area : 사용자가 생성하는 Java Object들이 저장되는 공간, 동적으로 할당하여 사용되어짐.
- Stack Area : 메소드 호출시 해당 메소드의 매개변수, 지역변수, 임시변수 등을 저장하기 위한 Stack 구조의 메모리.
- Native Heap Area : Java Object가 아닌 Native Object들이 거주하는 공간. OS 차원에서 결정.
- Permanent Space : Class에 대한 Meta 정보를 저장하는 공간. (Permanent Space는 Java Heap의 하위 영역)


* Java 실행 Option 

1. -X Option (모든 VM에서 동작하지 않을 수 있는 비표준 option이며, 버젼별로 언급없이 변경되어질 수 있음)
-Xms : 초기 Heap size 설정
-Xmx : 최대 Heap size 설정
-Xss : 각 Thread에 할당되는 Stack size 설정
-Xmn : New 영역을 위한 Heap size 설정

2. -XX Option (올바른 동작을 위해 특정한 시스템 요구사항들이 있으며, 시스템 설정 파라미터에 대한 접근 권한이 요구됨)
-XX:PermSize : 초기 Permanent size 설정
-XX:MaxPermSize : 최대 Permanent size 설정

※ 참고 사이트 : http://blogs.sun.com/watt/resource/jvm-options-list.html
                           http://www.monosun.com/doc/hotspotvmoption.html


* Heap Size 구하기

long   heapSize   = Runtime.getRuntime().totalMemory();
String heapSizeMB = (heapSize / (1024*1024)) + "MB";


* 영역별 OutOfMemoryError 대처 방법

1. Heap Area
Exception in thread "main": java.lang.OutOfMemoryError: Java heap space
Exception in thread main: java.lang.OutOfMemoryError: Requested array size exceeds VM limit


원인 : Heap size의 부족으로 Java Object를 생성하지 못하여 발생
해결 : 1. -Xmx Option을 이용하여 최대 Heap size의 크기를 늘려줌
           2. Application 로직이 잘못되었거나 JDK나 WAS의 Bug로 인한 Memory 누수가 있는지 확인하여 수정
           3. finalize method에 의해 즉각적인 GC가 이루어지지 않으므로 로직 수정을 통해 해결

※ Object Allocation Profiling (Hprof)
java -Xrunhprof:heap=sites [Main Class] 
java -Xrunhprof:heap=sites,doe=n [Main Class] (Thread Dump 생성)


2. Permanent SpaceException in thread "main": java.lang.OutOfMemoryError: Perm Gen space'

원인 : Permanent 저장 공간이 부족하여 발생.
          JSP -> Servlet 변환, Reflection을 사용하여 동적으로 로딩되는 Class가 많은 경우에 발생할 수 있으며,
          WAS의 Class Reloading 기능이 자주 실행 될 경우에도 발생할 수 있음.
해결 : -XX:PermSize, -XX:MaxPermSize Option을 이용하여 크기를 늘려줌

※ Class Loading Monitoring
java 실행시 -verbose:gc을 사용하여 Class가 Loading 되는 것을 Monitoring


3. Native Heap Area
java.lang.OutOfMemoryError: request bytes for . Out of swap space?
java.lang.OutOfMemoryError: (Native method)'
java.lang.OutOfMemoryError: unable to create new native thread


원인 : Native Heap memory가 부족하여 발생
해결 : 1. Physical memory를 초과할 경우 Virtual Memory를 요청하여 필요한 메모리를 확보하게 되는데,
             이 과정에서 오류가 발생할 경우 OS가 제공하는 툴을 통해 이를 모니터링 하고
             이 공간 자체가 부족할 경우 크기를 늘려줌
          2. -Xmx Option을 이용하여 Heap Area 공간을 줄이고 Native Heap Area 공간을 늘림
          3. Thread Stack Space가 부족한 경우 Thread의 수를 줄이거나,
              -Xss Option을 통해 Thread별 Stack Size를 줄여줌
              (단, Thread별 Stack Size를 과도하게 줄였을 경우 Stack Overflow Error가 발생할 수 있음)



참고 사이트 : http://ukja.tistory.com/61
                      http://2005elc.elancer.co.kr/eTimes/page/eTimes_view.html?str=c2VsdW5vPTIwODY=


ndk 의 필요성 (안드로이드 네이티브 영역)



안드로이드에 완벽하지 않은 네이티브..


NDK란 무엇인가?
NDK (Native Development Kit) 의 약자다.
쉽게 말해 java보다 네이티브한 영역인 C 영역에서 프로그래밍을 하는것을 말한다.
기본적으로 C와 java를 JNI 라는 인터페이스를 통해서 연결을 하는것이다. 

흔히들 자바를 해보지 않은 개발자들은 이런 말을 한다.
"야. C만 가지고는 안드로이드 개발 못하냐?"
일단 결론부터 얘기하면 "가능하다" 이다.
바로 위에서 얘기했던 NDK를 가지고다.
하지만 몇가지 제약조건이 있다.
일단 안드로이드의 기본 Window인 activity를 만들기 위해서는 
이 해더파일을 인클루드 해야 하는데 저 해더파일은 안드로이드 2.3부터 지원한다.
한마디로 완벽하게 C/C++을 가지고 안드로이드 프로그래밍을 하려면 2.3이상의 단말에서만 가능하다 ㅎㅎ;
참고로 opengl es 1.x는 1.6이상의 버전에서 2.x는 2.0 이상의 단말에서 지원을 한다.
한마디로 하위버전을 고려하지 않을거면 C로 짜세요. 가 맞을지도 ㅎㅎ;;

그리고 메모리에 대한 문제도 있다.
안드로이드의 메모리 영역은 쉽게 두가지로 나눌 수 있는다. 
자바의 allocation heap과 C에서 사용하는 native heap 이 이것이다.

안드로이드 개발자가 대부분 많이 격는 문제가 하나가 있는데 이미지를 불러오다 보면 OutofMemory를 많이 볼것이다.
그럼 기존 자바만 개발하던 개발자들은 하나같이 이런 말은 한다... "왜? 메모리가 이렇게 많은데? 왜?"

그건 바로 native heap이 부족해서이다. 안드로이드는 기본적으로 이미지같은 (Bitmap, Drawable 등) 걸 사용할때 native heap을 사용한다.
자바 영역에 heap이 아무리 남아 돌아도 native heap이 부족하면 바로 out of memory Exception이 난다. ;; 헐~

실제로 Drawable 안에는 Bitmap이 들어있고 Bitmap은 내부적으로 Skia 라는 라이브러리를 사용하고 이것은 native 영역으로 되어 있다.
(스키아가 궁금하면 받아서 확인해 볼것... git clone git://android.git.kernel.org/platform/external/skia.git)
한마디로 어디서 이미지를 사용하든지 간에 안드로이드에서 이미지를 사용하게 되면 native heap을 자신도 모르게 할당을 받아서 사용한다.
그래서 Bitmap 클레스에는 recycle() 이란 함수가 존재하는데 이 녀석에 역할은 native로 할당된 이미지 메모리 영역을 바로 free 시켜주는 역할을 한다.
한마디로 자바에서도 C처럼 메모리를 릴리즈 시켜줄수 있다는 얘기다. 

native heap 역시 자바의 vm heapsize 를 따르기때문에 heapsize 30m 라면 native heap도 30m까지밖에 할당을 하지 못한다. 
아까도 얘기했지만 30m를 vm의 heap과 나누어 쓰는것이 아니고 영역자체가 틀리기 때문에 각 각 30m 의 heap 생긴다고 생각하면 된다.

실제로 개발을 하면서 java의 heap이 부족해서 죽는 경우는 매우 드물다. 신경을 써야 할 부분은 바로 native heap이 중요하다.
아이폰에 경우에는 디바이스가 허용하는 범위라는 제약이 따르지만 안드로이드는 아이폰과 다르게 아무리 native라 하더라도 maxium heap 이 존재하기 때문에 큰 이미지를 불러온다던지, 이미지를 계속 불러오다보면 언젠가는 죽는다... 이런!!!
그래서 게임개발에 매우 취약하여 아이폰에 비해 게임이 많이 개발이 되지 않는 이유가 그래서이다. ㅠㅠ

기술이 발전하면서 안드로이드 단말의 heapsize도 계속 증가하지만. 그래도 아직까지는 개발자가 고심을 하면서 개발해야 하는 부분에 대해선 변함이 없다
출처 : http://blog.naver.com/anywars/140130318506