(익명의) 내부 클래스를 사용하는 것이 누출에 안전한 때는 정확히 언제입니까?
안드로이드 메모리 누수에 대한 기사를 읽고 구글 I/O에서 이 주제에 대한 흥미로운 비디오를 보았습니다.
그러나 저는 그 개념을 완전히 이해하지 못하며, 특히 액티비티 내의 사용자 내부 클래스가 안전하거나 위험한 경우에는 더욱 그렇습니다.
저는 다음과 같이 알고 있습니다.
메모리 누수는 내부 클래스의 인스턴스가 외부 클래스(액티비티)보다 오래 존속하면 발생합니다.-> 어떤 상황에서 이런 일이 일어날 수 있나요?
가 확장되는 이 없기 누출의 합니다.OnClickListener
동보보다다???????
final Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog_generic);
Button okButton = (Button) dialog.findViewById(R.id.dialog_button_ok);
TextView titleTv = (TextView) dialog.findViewById(R.id.dialog_generic_title);
// *** Handle button click
okButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
dialog.dismiss();
}
});
titleTv.setText("dialog title");
dialog.show();
이 예는 위험한가요? 왜 그런가요?
// We are still inside an Activity
_handlerToDelayDroidMove = new Handler();
_handlerToDelayDroidMove.postDelayed(_droidPlayRunnable, 10000);
private Runnable _droidPlayRunnable = new Runnable() {
public void run() {
_someFieldOfTheActivity.performLongCalculation();
}
};
이 토픽을 이해하는 것은 액티비티가 파괴되고 재생성되었을 때 무엇이 유지되는지 자세히 이해하는 것과 관련이 있다는 점에 대해 의문이 듭니다.
그런가요?
예를 들어 디바이스의 방향을 변경했다고 합시다(누출의 가장 일반적인 원인입니다).super.onCreate(savedInstanceState)
것이다onCreate()
그러면 필드의 값이 복원됩니까(방향 변경 전 상태 그대로)?이것은 또한 내부 계층의 상태를 회복시킬 것인가?
제 질문이 정확하지 않다는 것을 알지만, 상황을 더 명확하게 설명할 수 있다면 정말 감사하겠습니다.
당신이 묻고 있는 것은 꽤 어려운 질문입니다.당신은 그것이 단지 하나의 질문이라고 생각할 수 있지만, 실제로는 동시에 여러 질문을 하고 있다.제가 취재해야 한다는 것을 알고 최선을 다하겠습니다. 그리고 제가 놓칠 수 있는 것을 취재하기 위해 다른 사람들도 함께하길 바랍니다.
중첩된 클래스:서론
자바에서 OOP에 얼마나 익숙하실지 모르기 때문에 몇 가지 기본적인 사항을 말씀드리겠습니다.중첩 클래스는 클래스 정의가 다른 클래스에 포함된 경우입니다.기본적으로 두 가지 유형이 있습니다.스태틱 네스트 클래스 및 내부 클래스.이들 간의 실제 차이는 다음과 같습니다.
- '이것'은 다음과 같습니다.
- "최상위 수준"으로 간주됩니다.
- 포함하는 클래스의 인스턴스를 구성할 필요가 없습니다.
- 포함된 클래스 멤버를 명시적으로 참조하지 않으면 참조할 수 없습니다.
- 그들만의 삶을 가지세요.
- '이것'은 다음과 같습니다.
- 항상 포함하는 클래스의 인스턴스를 구성해야 합니다.
- 포함된 인스턴스에 대한 암묵적인 참조가 자동으로 생성됩니다.
- 참조 없이 컨테이너의 클래스 구성원에 액세스할 수 있습니다.
- 수명은 컨테이너의 수명보다 짧아야 합니다.
가비지 수집 및 내부 클래스
가비지 컬렉션은 자동으로 실행되지만 사용 중인 것으로 간주되는지 여부에 따라 개체를 제거하려고 합니다.가비지 콜렉터는 꽤 똑똑하지만 흠잡을 데 없는 것은 아니다.객체에 대한 활성 참조가 있는지 여부에 따라 무언가가 사용되고 있는지 여부만 판단할 수 있습니다.
여기서 진짜 문제는 내부 클래스가 컨테이너보다 더 오래 생존해 있을 때입니다.이는 포함된 클래스에 대한 암묵적인 참조 때문입니다.이 문제가 발생할 수 있는 유일한 방법은 포함 클래스 외부에 있는 개체가 포함 개체에 관계없이 내부 개체에 대한 참조를 유지하는 것입니다.
이로 인해 내부 객체가 (참조를 통해) 활성 상태이지만 포함된 객체에 대한 참조가 다른 모든 객체에서 이미 제거된 상황이 발생할 수 있습니다.따라서 내부 개체는 항상 해당 개체를 참조하기 때문에 포함된 개체를 활성 상태로 유지합니다.이 문제의 문제는 프로그램이 설정되어 있지 않으면 포함된 오브젝트로 돌아가 동작하고 있는지 여부를 확인할 수 없다는 것입니다.
이 실현의 가장 중요한 측면은 액티비티에 있든 드로잉 가능한 것이든 아무런 차이가 없다는 것입니다.내부 클래스를 사용할 때는 항상 체계적이고 컨테이너의 개체보다 오래가지 않도록 해야 합니다.다행히 코드의 핵심 객체가 아니라면 누출이 작을 수 있습니다.불행히도, 이것들은 발견하기 가장 어려운 유출들 중 일부입니다. 왜냐하면 그것들은 많은 유출이 될 때까지 눈에 띄지 않을 가능성이 높기 때문입니다.
솔루션:내부 클래스
- 포함된 개체에서 임시 참조를 가져옵니다.
- 포함된 개체만 내부 개체에 대한 긴 참조를 유지할 수 있습니다.
- 공장 등의 확립된 패턴을 사용합니다.
- 내부 클래스가 포함된 클래스 멤버에 액세스할 필요가 없는 경우 해당 클래스를 스태틱클래스로 변경하는 것을 검토해 주세요.
- 액티비티에 있는지 여부에 관계없이 주의하여 사용하십시오.
액티비티와 뷰: 개요
액티비티에는 실행 및 표시할 수 있는 많은 정보가 포함되어 있습니다.활동은 보기를 가져야 하는 특성에 따라 정의됩니다.또, 특정의 자동 핸들러도 있습니다.지정 여부에 관계없이 활동에는 포함된 보기에 대한 암묵적인 참조가 있습니다.
보기를 작성하려면 보기를 작성할 위치와 표시할 수 있는 하위 항목이 있는지 알아야 합니다., 액티비티를 하고 있음을 합니다(「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 「 」 。getContext()
에 대한 참조를 유지합니다.getChildAt()
마지막으로, 각 보기는 해당 표시를 나타내는 렌더링된 비트맵에 대한 참조를 유지합니다.
활동(또는 활동 컨텍스트)에 대한 참조가 있을 때마다 레이아웃 계층을 따라 전체 체인을 추적할 수 있습니다.액티비티 또는 뷰에 관한 메모리 누설이 큰 이유이기도 합니다.한 번에 엄청난 양의 메모리가 유출될 수 있습니다.
액티비티, 뷰 및 내부 클래스
위의 Inner Class에 대한 정보를 고려할 때, 이것들은 가장 일반적인 메모리누전이지만, 가장 일반적으로 회피되는 것입니다.내부 클래스가 액티비티 클래스의 멤버에 직접 액세스 할 수 있는 것이 바람직하지만, 많은 경우 잠재적인 문제를 피하기 위해 스태틱하게 만들 수 있습니다.액티비티와 뷰의 문제는 그것보다 훨씬 더 심각합니다.
유출된 액티비티, 뷰 및 액티비티 컨텍스트
결론은 콘텍스트와 라이프 사이클입니다.활동 컨텍스트를 종료하는 특정 이벤트(예: 방향)가 있습니다.많은 클래스나 메서드가 콘텍스트를 필요로 하기 때문에 개발자는 콘텍스트에 대한 참조를 가져와 유지함으로써 일부 코드를 저장하려고 할 수 있습니다.다만 액티비티를 실행하기 위해 작성해야 하는 오브젝트의 대부분은 액티비티 라이프 사이클 외부에 존재해야 합니다.오브젝트가 삭제되었을 때 액티비티, 컨텍스트 또는 뷰 중 하나에 대한 참조가 있는 경우, 액티비티와 그 전체 뷰 트리가 유출된 것입니다.
솔루션:액티비티와 뷰
- 어떤 경우에도 보기 또는 활동을 정적으로 참조하지 마십시오.
- 액티비티 콘텍스트에 대한 모든 참조는 단기간(기능 지속시간)이어야 합니다.
- 콘텍스트가 는, 콘텍스트 「」, 「」)를 합니다.
getBaseContext()
★★★★★★★★★★★★★★★★★」getApplicationContext()
이치노 - 또는 구성 변경을 재정의하여 활동 파괴를 제한할 수 있습니다.그러나 다른 잠재적 이벤트가 활동을 파괴하는 것을 막지는 못합니다.이것을 실시할 수 있지만, 상기의 프랙티스를 참조해 주세요.
실행 가능:서론
런너블은 사실 그렇게 나쁘지 않다.그럴 수도 있지만 이미 대부분의 위험 지역에 도달했어요실행 가능은 생성된 스레드와 독립적으로 태스크를 수행하는 비동기 작업입니다.대부분의 실행 가능 파일은 UI 스레드에서 인스턴스화됩니다.기본적으로 Runnable을 사용하면 다른 스레드가 생성되고 관리 수준이 약간 향상됩니다.Runnable을 표준 클래스처럼 분류하고 위의 지침을 따르면 문제가 거의 발생하지 않습니다.현실은 많은 개발자들이 이것을 하지 않는 것이다.
많은 개발자들은 쉽고 읽기 쉬우며 논리적인 프로그램 흐름을 위해 Anonymous Inner Class를 사용하여 위에서 작성한 예시와 같이 실행 가능 항목을 정의합니다.그러면 위에서 입력한 것과 같은 예가 됩니다.익명 내부 클래스는 기본적으로 개별 내부 클래스입니다.완전히 새로운 정의를 작성할 필요는 없으며 적절한 메서드를 덮어쓰기만 하면 됩니다.다른 모든 측면에서 내부 클래스입니다. 즉, 컨테이너에 대한 암묵적인 참조가 유지됩니다.
실행 가능 항목 및 작업/보기
예! 이 구간은 짧을 수 있어요!Runnables는 현재 스레드 외부에서 실행되므로 비동기 작업이 오래 실행될 위험이 있습니다.실행 가능이 액티비티 또는 뷰에서 익명 내부 클래스 또는 중첩된 내부 클래스로 정의된 경우 몇 가지 심각한 위험이 있습니다.이는 앞서 말한 바와 같이 컨테이너가 누구인지 알아야 하기 때문입니다.방향 변경(또는 시스템 중지)을 입력합니다.이제 이전 항을 참조하여 방금 무슨 일이 일어났는지 파악해 보십시오.네, 당신의 예는 꽤 위험합니다.
솔루션:Runnables
- 만약 너의 코드의 논리를 깨지 않고 확장하거나Runnable 보세요.
- 만약 해야 할 중첩 클래스 확장 Runnables 정적을 만들기 위해 최선을 다해라.
- 만약 당신이 익명의 Runnables를 사용해야 하는 사용에 있는 활동이나 보기에 장수하는 참조를 가지고 있는 어떤 개체에서를 창조하지 않다.
- 많은 Runnables 수 있을 쉽게 된 AsyncTasks.그 VM기본적으로 관리되는 것처럼 AsyncTask 사용을 검토한다.
이제 직접 이 편지의 다른 섹션에서 다루지 않던 질문에 답하는 파이널 질문 받는 상황입니다당신은"언제 내부에 클래스의 개체가 외부 수업보다 더 오래 살아남을 수 있을까요?" 물었다.전에 우리가 이것에 받지만 이것에 대해 활동에 걱정하는 게 옳고, 저 다시 강조하다:, 그 어디에도 누전을 일으킬 수 있도록 하겠습니다.그냥을 보여 주기 위해 간단한 예( 활동을 사용하지 않고)를 제공해야 한다.
아래는 기본적인 공장(코드를 실종)의 일반적인 예가 있다.
public class LeakFactory
{//Just so that we have some data to leak
int myID = 0;
// Necessary because our Leak class is an Inner class
public Leak createLeak()
{
return new Leak();
}
// Mass Manufactured Leak class
public class Leak
{//Again for a little data.
int size = 1;
}
}
이것은으로 흔하지 않기 예시이지만, 충분히 증명하기도 쉽다.여기서 핵심은 생성자가...
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Gotta have a Factory to make my holes
LeakFactory _holeDriller = new LeakFactory()
// Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//Store them in the class member
myHoles[i] = _holeDriller.createLeak();
}
// Yay! We're done!
// Buh-bye LeakFactory. I don't need you anymore...
}
}
이제, 우리는 비밀 누설이 없는 공장을 가지고 있다.비록 저희가 그 공장을 발표했다. 왜냐하면 누설 그것에 대한 기준이 있다면, 그것은 기억 속에 남을 것이다.심지어 외부 수업 자료가 전혀 없는 누구였든 상관 없어.이런 일은 생각보다 자주 일어난다.우리는 창조자가 아니라 창조자의 창조물만 필요하다.그래서 우리는 임시로 하나를 만들지만, 그 창조물을 무기한으로 사용합니다.
컨스트럭터를 조금만 바꾸면 어떤 일이 일어날지 상상해 보세요.
public class SwissCheese
{//Can't have swiss cheese without some holes
public Leak[] myHoles;
public SwissCheese()
{//Now, let's get the holes and store them.
myHoles = new Leak[1000];
for (int i = 0; i++; i<1000)
{//WOW! I don't even have to create a Factory...
// This is SOOOO much prettier....
myHoles[i] = new LeakFactory().createLeak();
}
}
}
새 Leak Factories가 모두 유출되었습니다.그것에 대해 어떻게 생각해요?그것들은 어떻게 내부 계층이 외부 계층보다 오래 살 수 있는지를 보여주는 매우 흔한 두 가지 예입니다.만약 그 외부 수업이 활동이었다면 얼마나 더 나빴을지 상상해 보세요.
결론
이 목록에는 이러한 개체를 부적절하게 사용할 경우 주로 알려진 위험이 나열되어 있습니다.일반적으로 이 게시물은 당신의 질문을 대부분 다뤘어야 했지만, 저는 이 게시물이 루엉 게시물이었다는 것을 알고 있기 때문에, 만약 설명이 필요하다면 저에게 알려주세요.위의 절차를 따르면 누출의 우려가 거의 없습니다.
1개의 투고에 2개의 질문이 있습니다.
- 내부 클래스를 사용하지 않고는 안전하지 않습니다.
static
Android 뿐만이 아니라, Java 세계 전체에 적용할 수 있습니다.
자세한 설명은 이쪽
사용 여부를 확인하기 위한 일반적인 내부 클래스 예제static class InnerAdapter
아니면 그냥class InnerAdapter
are 리스트)ListView
또는RecyclerView
, 탭 + 페이지 레이아웃 (ViewPager
드롭다운 및 AsyncTask 서브클래스
- Handler + Runnable, AsyncTask, RxJava 등의 사용 여부에 관계없이 Activity/Fragment/View가 파기된 후 작업이 완료되면 가비지 수집이 불가능한 Activity/Fragment/View 오브젝트(크다)의 루지 참조가 생성됩니다(프리할 수 없는 메모리 슬롯).
따라서 에서 장기간 실행되는 작업을 반드시 취소하십시오.onDestroy()
메모리 누수가 발생하지 않도록 하기 위해서입니다.
내부(익명) 클래스가 외부 클래스의 라이프 사이클이 짧거나 정확하게 동일하다는 것을 알면 안전하게 사용할 수 있습니다.
예를 들어,setOnClickListener()
Android 버튼의 경우 참조를 유지하는 다른 오브젝트가 없고 청취자 내부에서 긴 프로세스를 수행하지 않기 때문에 대부분의 경우 익명 클래스를 사용합니다.일단 외부 클래스가 파괴되면 내부 클래스도 파괴될 수 있습니다.
또 다른 예는 메모리 누수 문제는 안드로이드고 있다.LocationCallback
타격 예로.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initLocationLibraries();
}
private void initLocationLibraries() {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
mSettingsClient = LocationServices.getSettingsClient(this);
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
// location is received
mCurrentLocation = locationResult.getLastLocation();
updateLocationUI();
}
};
mRequestingLocationUpdates = false;
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(mLocationRequest);
mLocationSettingsRequest = builder.build();
}
}
이제 유일한지 않활동 LocationCallback의 기준을 보유하고 있는 안드로이드 지구 서비스 또한이 떨어진다.지구 대기 서비스 활동보다 더 오래 인생을 가지고 있다.그것은 활동에 메모리 누수를 야기할 것이다.
더 자세한 내용이 여기 설명되어 있다.
언급URL:https://stackoverflow.com/questions/10864853/when-exactly-is-it-leak-safe-to-use-anonymous-inner-classes
'programing' 카테고리의 다른 글
왜 Double일까요?NaN==더블.NaN 반환이 거짓입니까? (0) | 2022.09.01 |
---|---|
글로벌 vuex 워처 (0) | 2022.09.01 |
Java에서 List 개체를 초기화하려면 어떻게 해야 합니까? (0) | 2022.08.31 |
Java 다중 상속 (0) | 2022.08.31 |
Eclipse Java 디버깅: 소스를 찾을 수 없습니다. (0) | 2022.08.31 |