在React原生JavaScript应用程序中,如果我创建一个临时变量,而不是直接返回值,为什么Android GC行为会改变?

在React原生JavaScript应用程序中,如果我创建一个临时变量,而不是直接返回值,为什么Android GC行为会改变?,javascript,android,react-native,garbage-collection,Javascript,Android,React Native,Garbage Collection,我觉得Android GC、JavaScriptCore或Genymotion中可能存在bug。我正在Android上测试React本机应用程序,所以我所有的代码都是用JavaScript(而不是Java)编写的。我试图找出我在以下Android模拟器上复制的崩溃: 三星Galaxy S6-6.0.0-API 23 谷歌Nexus5x-7.1.0-API 25 我注意到我的应用程序在使用了大约5分钟后总是崩溃。在使用adb logcat查看日志后,我注意到它总是在GC运行后立即崩溃。它只是在

我觉得Android GC、JavaScriptCore或Genymotion中可能存在bug。我正在Android上测试React本机应用程序,所以我所有的代码都是用JavaScript(而不是Java)编写的。我试图找出我在以下Android模拟器上复制的崩溃:

  • 三星Galaxy S6-6.0.0-API 23
  • 谷歌Nexus5x-7.1.0-API 25
我注意到我的应用程序在使用了大约5分钟后总是崩溃。在使用adb logcat查看日志后,我注意到它总是在GC运行后立即崩溃。它只是在安卓系统上崩溃,而在iOS系统上从未发生过崩溃。只有当“远程调试JS”被关闭时,这种情况才会发生,我才意识到原因。这是因为当启用“远程调试JS”时。(这使得调试非常困难!)

我正在使用库,但我正在使用一个具有可配置“cacheSize”的分支来存储多个结果,而不仅仅是缓存单个结果。你可以。我的第一个想法是我在这里做错了什么,但我没有使用
WeakMap
或类似的东西。我只是将缓存的结果存储在一个普通数组中,并且我总是保存对选择器的引用,所以我认为GC不应该清除内存

我将介绍有关我的应用程序的更多详细信息。我有一个
LookupTable
类,它引用一个
Matrix
类。查找表预先计算了一些内容,以便更快地查找

我的查找表代码如下所示:

// @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))
const lookupTable = lookupTableSelector(state)
const value = lookupTable.valueAt(x, y)
我的原始选择器代码如下所示:

// @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))
const lookupTable = lookupTableSelector(state)
const value = lookupTable.valueAt(x, y)
如果您不熟悉
reselect
immutable js
,我只是在记忆一些计算(类似于lodash的
memoize
),我使用
immutable.is
检查参数相等性

最后,我会这样调用选择器:

// @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))
const lookupTable = lookupTableSelector(state)
const value = lookupTable.valueAt(x, y)
Android GC运行后,
valueAt
导致崩溃,因为
this.matrix
未定义。有趣的是,
lookupTable.generateLookupTable
函数也没有定义。事实上,实例中的所有内容都是未定义的。就像选择器只是返回一个
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运行测试了该应用程序,一切都很顺利。所以无论我在这里做了什么,我想它改变了安卓GC的行为,不再为我的
LookupTable
实例清除内存

你能理解为什么会发生这种情况吗?或者这可能是Android GC中的引用计数错误