In a React Native JavaScript app, why does the Android GC behavior change if I create a temporary variable instead of a direct value?

I feel that there might be a bug somewhere in Android GC, JavaScriptCore, or perhaps in Genymotion. I am testing a React Native application on Android, so all my code is written in JavaScript (not Java). I am trying to figure out what crash I reproduced on the following Android emulators:

  • Samsung Galaxy S6 - 6.0.0 - API 23
  • Google Nexus 5X - 7.1.0 - API 25

I noticed that my application always crashed after I used it for about 5 minutes. Looking at the logs with adb logcat, I noticed that it always crashes immediately after starting the GC. It was just a crash on Android, and the crash never happened on iOS. This also happened when JS Debugging Remotely was disabled, and I just realized why. This is because when "JS Debugging remotely" is turned on, all JS runs inside Chrome on my laptop in the V8 engine . (Because of which it is quite difficult to debug!)

I use the reselect library , but I use the custom branch "cacheSize" to store multiple results, instead of just caching a single result. You can see my new feature defaultMemoizehere . My first thought is that I did something wrong, but I do not use WeakMapor something like that. I just store the cached results in a regular array, and I always keep a reference to the selector, so I don't think GC should clear the memory.

I will talk about some of the details of my application. I have a class LookupTablethat references a class Matrix. The lookup table pre-computes some things for faster searches.

My view table code looks something like this:

// @flow
import autobind from 'autobind-decorator'

export default class LookupTable {
  matrix: Matrix

  constructor(state) {
    this.generateLookupTable(state)
  }

  @autobind
  valueAt(x: number, y: number) {
    this.matrix.get(x, y)
  }

  @autobind
  generateLookupTable() {
    // generates the Matrix at this.matrix
  }
}

:

import Immutable from 'immutable'
import { createSelectorCreator, defaultMemoize } from 'reselect'

const createImmutableSelector = (cacheSize = 1, ...args) =>
  createSelectorCreator(defaultMemoize, Immutable.is, cacheSize)(...args)

export const lookupTableSelector = createImmutableSelector(3,
  firstSelector,
  secondSelector,
  thirdSelector,
  fourthSelector,
  (one, two, three, four) =>
    new LookupTable(one, two, three, four))

reselect immutable-js, ( lodash memoize), Immutable.is .

, :

const lookupTable = lookupTableSelector(state)
const value = lookupTable.valueAt(x, y)

Android GC valueAt , this.matrix undefined. , lookupTable.generateLookupTable undefined. , undefined. LookupTable, .

, :

export const lookupTableSelectorWithoutGCFix = createImmutableSelector(3,
  firstSelector,
  secondSelector,
  thirdSelector,
  fourthSelector,
  (one, two, three, four) =>
    new LookupTable(one, two, three, four))


export const lookupTableSelector = (gameState: Map) => {
  const lookupTable = lookupTableSelectorWithoutGCFix(gameState)

  if (lookupTable.matrix == null) {
    console.warn('LookupTable selector returned an instance with an undefined matrix.' +
      'This might be a GC bug on Android. Will clear the cache and generate a new instance.')
    lookupTableSelectorWithoutGCFix.clearCache()
    return lookupTableSelectorWithoutGCFix(gameState)
  }

  return lookupTable
}

, . adb logcat, . GC. 5 GC, . , , , Android GC, LookupTable.

, ? , , Android GC?

+4

Source: https://habr.com/ru/post/1675040/


All Articles