Java는 쓰레기 수집 언어의 한 유형입니다. 이 장점은 개발자가 의도적으로 메모리 할당을 관리 할 필요가 없어서 로컬 분할 결함으로 인해 응용 프로그램이 충돌 할 가능성을 줄이고 스택 (HEAP)을 압박하는 것을 방지한다는 것입니다. 따라서 작성된 코드는 더 안전합니다.
불행히도, Java에는 메모리 누출이 발생하기 쉬운 많은 논리적 누출이 여전히 많이 있습니다. 조심하지 않으면 안드로이드 애플리케이션이 쉽게 메모리를 낭비 할 수있어 결국 메모리가 발생하는 오류가 발생할 수 있습니다 (Memory, OOM).
1. 일반 메모리 누출의 이유는 객체에 대한 모든 참조가 해제되면 객체가 해제되지 않았기 때문입니다. (번역기 주 : Cursor는 닫는 것을 잊었습니다.)
2. 논리적 메모리 누출의 이유는 응용 프로그램에 더 이상이 개체가 필요하지 않을 때 객체에 대한 모든 참조가 해제되지 않았기 때문입니다.
물체에 대한 강한 참조를 사용하면 쓰레기 수집기가 메모리에서 물체를 재활용 할 수 없습니다.
안드로이드 개발에서 가장 가능성이 높은 메모리 누출 문제는 컨텍스트입니다. 예를 들어, 활동의 컨텍스트에는 View 계층 및 기타 리소스와 같은 많은 메모리 참조가 포함되어 있습니다. 컨텍스트가 유출되면 모든 물체를 가리키는 것을 의미합니다. 안드로이드 머신은 메모리가 제한되어 있으며 너무 많은 메모리 누출로 인해 OOM이 쉽게 이어질 수 있습니다.
논리적 메모리 누출을 감지하려면 주관적인 판단이 필요합니다. 특히 물체의 수명주기는 명확하지 않습니다. 다행히도 활동에는 명확한 수명주기가 있으며 누출의 원인을 쉽게 찾을 수 있습니다. Activity.ondestroy ()는 활동 수명의 끝으로 간주됩니다. 프로그래밍 방식으로 파괴되어야하거나 Android 시스템 이이 메모리를 재활용해야합니다 (번역기 주 : 메모리가 불충분 한 경우 Android는 보이지 않는 활동을 재활용합니다).
이 방법이 실행되면 여전히 스택의 활동에 대한 강력한 참조가 있으며 쓰레기 수집기는 재활용 메모리로 표시 할 수 없으며 원래의 목적은 재활용하는 것입니다!
결과적으로 활동은 수명주기 밖에서 살아 남았습니다.
활동은 헤비급 물체이며 Android 시스템에서 처리해야합니다. 그러나 논리적 메모리 누출은 항상 실수로 발생합니다. (Translator 's Note : 한 번은 메모리 누출을 일으킨 활동을 시도했습니다). 안드로이드에는 잠재적 인 메모리 누출로 이어지는 트랩이 두 개뿐입니다.
글로벌 프로세스의 정적 변수 (프로세스 글로벌). 응용 프로그램의 상태를 무시하고 활동에 대한 강력한 참조를 보유한이 괴물.
활동 수명주기 외부에 사는 실. 활동에 대한 강력한 언급은 없었다.
다음 상황이 발생했는지 확인하십시오.
1. 종종 활동
정적 활동 변수는 클래스에 정의되며 현재 실행중인 활동 인스턴스는이 정적 변수에 할당됩니다.
활동 수명주기가 끝난 후이 정적 변수가 지워지지 않으면 메모리 누출이 발생합니다. 정적 변수는이 애플리케이션의 수명주기를 통과하기 때문에 유출 된 활동은 항상 응용 프로그램 프로세스에 존재하며 쓰레기 수집기에 의해 수집되지 않습니다.
정적 활동 활동; void setStaticActivity () {activity = this;} view sabutton = findViewById (r.id.sa_button); sabutton.setonClickListener (new View.onClickListener () {@override public void on Click (setStaticActivity (); nextActivity ());2. 종종 관점
싱글 톤 모드에서도 비슷한 상황이 발생할 수 있으며 활동이 자주 사용되면 메모리에 인스턴스를 저장하는 것이 실용적입니다. 앞에서 언급했듯이 활동의 수명주기를 강요하는 것은 매우 위험하고 불필요하며 어쨌든 할 수 없습니다.
특별한 경우 :보기 초기화가 많은 리소스를 소비하고 활동 수명주기 중에 변경되지 않은 상태로 유지되면, 정적으로 바꾸어 Hierachy에로드 될 수 있습니다. 이런 식으로 활동이 파괴되면 자원을 해제해야합니다. (Translator 's Note : 메모리는 샘플 코드에서 해제되지 않습니다.이 정적보기만으로도 정적보기 방법을 사용하는 것이 권장되지 않습니다).
정적보기; void setStaticView () {view = findViewById (r.id.sv_button);} view svbutton = findViewById (r.id.sv_button); svbutton.setonClickListener (new View.onClickListener () {@override public void on Click () {setstaticview (); }});3. 수업
계속해서, 활동에 내부 클래스가 있다고 가정하면, 그렇게하면 가독성과 캡슐화가 향상 될 수 있습니다. 우리가 내부 클래스를 생성하고 정적 변수에 대한 참조를 보유하면 축하합니다. 축하합니다. 메모리 누출은 당신과 그리 멀지 않습니다 (번역기의 메모 : 비어있을 때 비어 있음).
개인 정적 객체 내부; void createInnerClass () {클래스 내부 클래스 {} inner = new InnerClass ();} view icbutton = findViewById (r.id.ic_button); icbutton.setonClickListener (new View.onClickListener () {@override public void onclick (view v)) {) {);내부 클래스의 장점 중 하나는 외부 클래스에 액세스 할 수 있다는 것입니다. 불행히도, 메모리 누출의 이유는 내부 클래스가 외부 클래스 인스턴스에 대한 강력한 참조를 가지고 있기 때문입니다.
4. 신문 수업
마찬가지로 익명 수업은 외부 클래스에 대한 참조를 유지합니다. 따라서 활동에서 익명의 비동기를 정의 할 때 메모리 누출이 쉽습니다. 비동기적인 작업이 백그라운드에서 시간이 많이 걸리는 작업을 실행할 때, 불행히도 활동이 파괴됩니다 (번역기 주 : 사용자 종료, 시스템 재활용), 비동기 작업이 완료 될 때까지 Asynctask가 보유한이 활동 인스턴스는 쓰레기 수집기에 의해 재활용되지 않습니다.
void startAsyncTask () {new AsyncTask <void, void, void> () {@override protected void doinbackground (void ... params) {while (true); }} .Execute ();} super.oncreate (savedInstancestate); setContentView (r.layout.Activity_Main);보기 aicbutton = findViewById (r.id.at_button); aicbutton.setonClickListener (new View.onClickListener () {@override on void onclicklick (viewclicklistener) {aicbutton. STARTASYNCTASK ();5. 핸드러
같은 토큰으로 익명으로 실행 가능한 익명을 정의하고 익명 클래스 핸들러로 실행하십시오. 실행 가능한 내부 클래스는 외부 클래스에 대한 암시 적 참조를 보유하고 있으며 핸들러 메시지 큐의 메시지 큐에 전달됩니다. 메시지 메시지가 처리 될 때까지 활동 인스턴스가 파괴되지 않아 메모리 누출이 발생합니다.
void createHandler () {new Handler () {@Override public void handlemessage (메시지 메시지) {super.handleMessage (메시지); }} .postDelayed (new Runnable () {@override public void run () {while (true);}}, long.max_value >> 1);} view hbutton = findViewById (r.id.h_button); hbutton.setonclicklistener (new void.onclicklistener () { @clickllistener () { @clickllistener (r.id.h_button); v) {createHandler ();6. 스레드
우리는 다시 한 번 스레드와 타이머스크를 통해 메모리 누출을 보여줍니다.
void spawnthread () {new Thread () {@override public void run () {while (true); }} .start ();}보기 tbutton = findViewById (r.id.t_button); tbutton.setonClickListener (new View.onClickListener () {@override public void onclick (view v) {spawnthread (); nextActivity ();});7. TIMERTASK
익명의 클래스의 인스턴스 인 한, 작업자 스레드에 있는지 여부에 관계없이 활동에 대한 참조를 보유하여 메모리 누출이 발생합니다.
void scheduletimer () {new Timer (). 스케줄 (new Timertask () {@override public void run () {while (true);}}, long.max_value >> 1);} findViewById (r.id.tt_button); ttbutton.setonclickllestener (new.onclicklictner) @override public void onclick (View v) {scheduletimer ();8. 센서 관리자
마지막으로, 시스템 서비스는 context.getsystemservice (int name)를 통해 얻을 수 있습니다. 이러한 서비스는 해당 프로세스에서 작동하여 응용 프로그램이 배경 작업 및 하드웨어 상호 작용을 처리 할 수 있도록 도와줍니다. 이러한 서비스를 사용해야하는 경우 리스너를 등록하여 서비스가 컨텍스트에 대한 참조를 보유하게됩니다. 이러한 청취자가 활동이 파괴 될 때 로그 아웃하지 않으면 메모리 누출이 발생합니다.
void registerListener () {sensormanager sensormanager = (sensormanager) getsystemservice (sensor_service); 센서 센서 = sensormanager.getDefaultSensor (sensor.type_all); sensormanager.registerListener (이, 센서, sensormanager.sensor_delay_fastest);} view smbutton = findViewById (r.id.sm_button); smbutton.setonclicklistener (new View.onclicklistener () {@override public void on Click (view) {registerlistlistner () {registerlistlistlistlistner (); }});요약
메모리 누출로 이어질 수있는 많은 사례를 보았을 때 휴대 전화의 모든 메모리를 먹고 쓰레기 수집 및 처리를 더 자주 처리하기가 쉽고 최악의 경우에도 OOM으로 이어질 것입니다. 쓰레기 수집 작업은 비싸고 눈에 보이는 지연으로 이어질 수 있습니다. 따라서 인스턴스팅 할 때 Held Reference 체인에주의를 기울이고 종종 메모리 누출 점검을 수행하십시오.