LeakCanary, класс области видимости утечек подписки, переопределяющий «на месте». Возможно, это ложное утверждение... ноJAVA

Программисты JAVA общаются здесь
Ответить Пред. темаСлед. тема
Anonymous
 LeakCanary, класс области видимости утечек подписки, переопределяющий «на месте». Возможно, это ложное утверждение... но

Сообщение Anonymous »


Если бы я публиковал код, мне нужно было бы опубликовать как минимум 1 тыс. строк о 6 разных классах... и 3 разных архитектурах. Поэтому я дам вам как можно более краткую информацию... также утечку путем передачи (это ) кажется запоминающимся, поэтому я надеюсь, что кто-нибудь знаком с этой проблемой.
Я использую

Код: Выделить всё

com.squareup.leakcanary:leakcanary-android:3.0-alpha-1
(the latest).
A Lifecycle has a beginning and an end, each of which has inner phases, that begin and end in a stacked type FIFO manner.
Where each element has 2 states, a "creating" state and a "destroying" state.
Whenever an element is pushed, it adopts a creating state, meanwhile, at the moment of pop, it adopts a destroying state.
This means that there is a preestablish immovable sequence... something that Android lacks when dealing with lifecycle method callbacks inside Fragments (onAttach, onCreate, onSavedInstanceState)...

Код: Выделить всё

CREATE --> DESTROY
|          ^
v          |
START  -->  STOP
|          ^
v          |
RESUME --> PAUSE
Lifecycle A ->(contains)-> Lifecycle B ->(contains)-> Lifecycle C. ("contains" as in an Object Oriented inner component).
Lifecycle A BEGINS FIRST, then B, then C.
C will DIE FIRST than B, than A.
Like a Matryoshka doll of nested lifecycles.
THIS IS A CORRECT Lifecycle'd architecture. In a correct implementation, each of the destructive phases should be a MIRROR of its creating phases, and the events should be triggered in a FIFO manner...
If Lifecycle C needs to LISTEN something within Lifecycle A, IT's OBSERVER NEEDS to have the SAME LIFESPAN of its owner, in this case of C. (If you are familiar with Android's ViewLifeCycleOwner this is the principle.)
This avoids Lapsed Listeners... aka memory leaks.
BUT LISTENING is one thing...

Код: Выделить всё

register(t -> {});
and

Код: Выделить всё

unregister();
The 'opposite' of these reactive actions are... PROACTIVE actions. so... get(); and set(T val); are proactive actions.
In reality the "reactive" in a

Код: Выделить всё

register(t -> {});
action, ecompasses ONLY what is between the "" scope.
But the action itself... is proactive... the

Код: Выделить всё

unregister();
action is fully proactive.
Now let me present you with the false positive that is "leaking" according to LeakCanary...

Код: Выделить всё

private final LazyHolder.Supplier summaryVM2 =
syncActivator(Phase.resume,
acquireContext(
context -> new ByDaySummaryVM2(((MainActivity)
context).getModel()) {
@Override
protected void onStateChange(boolean isActive) {
if (isActive) {
memberStatsManager.setVM(this);
elementStatsManager.setVM(this);
}
}
}
)
);
this LazyHolder.Supplier will "wait" until

Код: Выделить всё

Phase.resume
to '

Код: Выделить всё

.activate()
' the Object mapped by the '

Код: Выделить всё

acquireContext(context ->
' method. The method actually maps the

Код: Выделить всё

Function
EARLIER than

Код: Выделить всё

Phase.resume
, the resume just "activates" it later.
Now... the

Код: Выделить всё

.setVM(this);
does something interesting.
Both managers build their own inner Lifecycled Objects... with a Lifespan "NARROWER/SHORTER" than the scope that owns the

Код: Выделить всё

LazyHolder.Supplier
, they do this during construction.
When

Код: Выделить всё

setVM(this);
is used, a stateless Function builds a BRANCH of interconnected reactive component nodes... it does so like this:...
At entry point... the same reactive library is used to capture itself.... this is the '' that is passed.

Код: Выделить всё

private final In.Consume builderConsumer = new In.Consume();
private final In.Consume type = new In.Consume();

private final Path resolve =
builderConsumer.switchMap(
type::map
);
The reactive component captures the ViewModel instance and maps it accordingly to the requirement of the Manager.
This means that there is a reactive architecture WITHIN it's own reactive architecture...
that... based on a 'displayType' value PLUS a displayResolve variable... создает серию преобразований и switchMaps, создавая целую новую реактивную ветвь, хранящуюся в.
THEN the 'leaves' of these newly created components are attached to the Lifecycle of the shorter-lived life-cycled objects.
To match the example:
The bigger scope (the class containing the

Код: Выделить всё

LazyHolder.Supplier
object) would be the Lifecycle A.
The view model

Код: Выделить всё

ByDaySummaryVM2
would be Lifecycle B.
Both Managers containing the smaller Objects, would be both Lifecycle C (even tho they may be more like a B.1 since their lifespans are more 'parallel' to that of the view-model).
When C dies, B will die after, then A... This is the moment that a "leak" appears.
Now...
The ViewModel (Lifecycle B) CONNECTS to a reactive source... this is the "WIDEST/LONGEST" Lifecycle of them all... "Lifecycle Zed"...
The issue...
It appears as if a Listener that belongs to "Lifecyle A"... (the outer scope), is attached DIRECTLY to something upstream (Lifecycle Zed).
So, the order of the "dominators" according to LeakCannary is:
A -> Zed.
paraphrasing the error message:

Код: Выделить всё

                    "Zed scope static storage"
LeakCanary               D  │    ↓ OuterScope$2.this$0
LeakCanary               D  │                             ~~~~~~
LeakCanary               D  ╰→ OuterScope instance
LeakCanary               D  ​     Leaking: YES (ObjectWatcher was watching this because OuterScope received Fragment#onDestroy() callback. Conflicts with Fragment.mLifecycleRegistry.state is
LeakCanary               D  ​     INITIALIZED)
LeakCanary               D  ​     Retaining 65.6 kB in 1980 objects
LeakCanary               D  ​     key = 9e3903cd-4cd8-4e9e-bdda-24ba91735925
LeakCanary               D  ​     watchDurationMillis = 960661
LeakCanary               D  ​     retainedDurationMillis = 955661
That message is weird... a "conflict in Lifecycle registry.state"???
I am not even touching the LifecycleRegistry!!
When testing the Observers registered one by one... the REALITY seems to be
A -> C -> B -> A -> Zed.
What's wrong with this indictment??? ...
There are NO listeners from A registered to Zed... None whatsoever.
IN THEORY:
Once the listeners (of C) connected to these NEWLY CREATED branches (created during B's initialization) ... disconnect... the entire newly created reactive branch... gets deactivated.
Then C gets popped, B gets popped... and finally A gets popped from their Collections...
The inactive reactive branch... will remain in the

Код: Выделить всё

metaspace
of the memory, the branch, remaining within A, B and C... which will ALSO be in the

Код: Выделить всё

metaspace
... until the Garbage Collector decides to collect them all.
Even if the inactive (newly created) branch (of the reactive architecture) that was built inside the 'A -> B -> C' structure remains tied to some portion of the reactive... active portion of the branch that's still being used("Lifecycle Zed")... it does so in a PROACTIVE manner... without any Observers registered.
So....
When the Virtual Machine acknowledges that interdependencies are NO LONGER connected, that CONTAIN instances of the branch (ABC structure) ... they SHOULD GET garbage collected... AS LONG as there are NO Observers of a "WIDER" lifespan registered to this reactive branch.
In this case the widest lifespan would be that of "Context"(Zed)... YET everything happening with Context... at the point of destruction... IS PROACTIVELY interacted with... (this is corroborated by the '2-second-delayed assertion' at the end of the solution bellow...)
**Will the reactive architecture WITHIN reactive architecture make a memory leak???...
NON whatsoever.... the same PROACTIVE rule applies.**
What is the fix???

Код: Выделить всё

private final LazyHolder.Supplier summaryVM2 =
syncActivator(Phase.resume,
acquireContext(
context ->
new FalsePositiveLeakagePrevention(
memberStatsManager
, elementStatsManager
, ((MainActivity)context).getModel()
)
)
);
Where FalsePositiveLeakage is:

Код: Выделить всё

//Both managers' lifecycle are shorter than that of OuterScope...  ДА, OuterScope считается "захваченным"
// узлом из статически хранимой ветки (в Zed) ЧЕРЕЗmemberStatsManager.
частный статический класс FalsePositiveLeakagePrevention расширяет ByDaySummaryVM2 {

частный окончательный менеджер
memberStatsManager;
частный окончательный менеджер elementStatsManager;

public FalsePositiveLeakagePrevention(
МенеджерmemberStatsManager
, Менеджер elementStatsManager
, Поставщик< Element.Constant.DAOS.MemoryModel> model
) {
super(model);
this.memberStatsManager =memberStatsManager;
this.elementStatsManager = elementStatsManager;
}< br />

protected Final void onStateChange(boolean isActive) {
if (isActive) {
memberStatsManager.setVM(this);
elementStatsManager.setVM(this) ;
 //Абсолютное подтверждение отсутствия утечки...
else {
Post.TWO_SEC_delay(
() -> {
try {
AssertNotActive();
} catch (Exception | Ошибка e) {
Log.println(Log.ERROR, TAG, "onStateChange: ");
выдать новое исключение RuntimeException(e);
  ) ;

>

Это позволит устранить «утечку памяти».
Могут быть некоторые причины, по которым это устраняет утечку...
  • Может быть, есть некоторые забавные вещи происходит в пространстве памяти,
    поскольку ViewModel объединяет 2 статически сохраненных источника во время выполнения....
    возможно, все построенное тело сохраняется в OldGeneration... и потому что действие
    выполняется в полувидимой области видимости OuterScope... область действия хранится в
    этом пространстве памяти... следовательно, выполняя эту транзакцию внутри статического отдельного тела, OuterScope больше не становится частью OldGen. (Это не объясняет предполагаемое несоответствие в состоянии реестра жизненного цикла)
  • Другой возможностью может быть ложное срабатывание, когда LeakCanary
    останавливается на

    Код: Выделить всё

    .setVM(this);
    and loops past the direct scope of the
    ViewModel in the reference graph and goes straight to the outer
    scope.
  • A third possibility is that the 'Reactive-Within-Reactive' is "messing up" the reference graph when analyzed by LeakCannary.
  • Now the fourth possibility is... there is still a memory leak... but
    LeakCannary cannot identify it, (unlikely because of the type of
    fix, and the corroboration on all fronts.).
Finallly... why is the static class preventing a LifecycleRegistry.State mismatch???


Источник: https://stackoverflow.com/questions/781 ... may-be-a-f
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «JAVA»