2

尝试查询最多 30 个对象时出现此错误,每个对象都有字段 byte[],其权重为 100x100 ARGB_8888 位图数据~ 39kb

我正在使用 OrmLite 4.45 版本。在三星 GT n8000 平板电脑上(最大堆大小 64mb)

这是堆栈跟踪:

android.database.sqlite.SQLiteException: unknown error (code 0): Native could not create new byte[]
    at android.database.CursorWindow.nativeGetBlob(Native Method)
    at android.database.CursorWindow.getBlob(CursorWindow.java:403)
    at android.database.AbstractWindowedCursor.getBlob(AbstractWindowedCursor.java:45)
    at com.j256.ormlite.android.AndroidDatabaseResults.getBytes(AndroidDatabaseResults.java:161)
    at com.j256.ormlite.field.types.ByteArrayType.resultToSqlArg(ByteArrayType.java:41)
    at com.j256.ormlite.field.BaseFieldConverter.resultToJava(BaseFieldConverter.java:24)
    at com.j256.ormlite.field.FieldType.resultToJava(FieldType.java:798)
    at com.j256.ormlite.stmt.mapped.BaseMappedQuery.mapRow(BaseMappedQuery.java:60)
    at com.j256.ormlite.stmt.SelectIterator.getCurrent(SelectIterator.java:270)
    at com.j256.ormlite.stmt.SelectIterator.nextThrow(SelectIterator.java:161)
    at com.j256.ormlite.stmt.StatementExecutor.query(StatementExecutor.java:187)
    at com.j256.ormlite.dao.BaseDaoImpl.query(BaseDaoImpl.java:263)
    at com.j256.ormlite.dao.EagerForeignCollection.(EagerForeignCollection.java:37)
    at com.j256.ormlite.field.FieldType.buildForeignCollection(FieldType.java:781)
    at com.j256.ormlite.stmt.mapped.BaseMappedQuery.mapRow(BaseMappedQuery.java:82)
    at com.j256.ormlite.android.AndroidDatabaseConnection.queryForOne(AndroidDatabaseConnection.java:186)
    at com.j256.ormlite.stmt.mapped.MappedQueryForId.execute(MappedQueryForId.java:38)
    at com.j256.ormlite.field.FieldType.assignField(FieldType.java:540)
    at com.j256.ormlite.stmt.mapped.BaseMappedQuery.mapRow(BaseMappedQuery.java:71)
    at com.j256.ormlite.stmt.SelectIterator.getCurrent(SelectIterator.java:270)
    at com.j256.ormlite.stmt.SelectIterator.nextThrow(SelectIterator.java:161)
    at com.j256.ormlite.stmt.StatementExecutor.query(StatementExecutor.java:187)
    at com.j256.ormlite.dao.BaseDaoImpl.query(BaseDaoImpl.java:263)
    at com.j256.ormlite.stmt.QueryBuilder.query(QueryBuilder.java:319)
    at com.j256.ormlite.stmt.Where.query(Where.java:485)
    at com.j256.ormlite.dao.BaseDaoImpl.queryForEq(BaseDaoImpl.java:243)    

这是logcat:

05-16 14:05:24.561: D/dalvikvm(4163): GC_CONCURRENT freed 1247K, 10% free 18046K/19911K, paused 11ms+3ms, total 30ms
05-16 14:05:24.561: D/dalvikvm(4163): WAIT_FOR_CONCURRENT_GC blocked 10ms
05-16 14:05:24.686: D/dalvikvm(4163): GC_CONCURRENT freed 119K, 4% free 19922K/20743K, paused 11ms+2ms, total 28ms
05-16 14:05:24.686: D/dalvikvm(4163): WAIT_FOR_CONCURRENT_GC blocked 15ms
... whole ton of these
05-16 14:05:27.261: D/dalvikvm(4163): GC_CONCURRENT freed 109K, 2% free 62754K/63495K, paused 12ms+5ms, total 36ms
05-16 14:05:27.261: D/dalvikvm(4163): WAIT_FOR_CONCURRENT_GC blocked 20ms
05-16 14:05:27.366: I/dalvikvm-heap(4163): Clamp target GC heap from 65.738MB to 64.000MB
  • 内存使用量增长如此之快正常吗?
  • 您如何看待将查询拆分为块并在这些单独的查询之间显式调用 System.gc() ?

谢谢!

4

1 回答 1

2

内存使用量增长如此之快正常吗?

不,不是。

您如何看待将查询拆分为块并在这些单独的查询之间显式调用 System.gc() ?

不,这很可能无法解决问题。您需要直接解决底层内存问题。

在查看了您未在帖子中提供的代码和实体之后,这不是ORMLite问题,而是实体问题。

你有一个Gallerys Photo。每张照片都有一个可能很大的字节图像数据数组——可能是 50+k。问题是sGallery有一个急切的外国集合Photo

@ForeignCollectionField(eager = true)
private ForeignCollection<Photo> photos;

然后每个Photo都有其 parent 的自动刷新版本Gallery

@DatabaseField(foreign = true, foreignAutoRefresh = true, columnName = GALLERY)
private Gallery gallery;

这会设置一个急切的 fetch 循环,导致 ORMLite 执行以下操作:

  1. 每当 ORMLite 尝试将其加载Gallery到内存中时...
  2. Gallery由于急切的收集,它被要求进行另一个查询并将与该相关联的所有照片加载到内存中。
  3. 对于这些实例中的每一个,由于自动刷新父级Photo,它被要求执行另一个查询以将关联的内容放入内存中。Gallery
  4. 为此,Gallery它被要求将所有Photos 加载到内存中。
  5. ... ORMLite 实际上有一个逃生,但仍然做到了这 3 个级别。

ORMLite 没有GalleryPhoto实例的神奇视图,因此它将父级附加Gallery到 s 中的外部字段Photo。如果你想要这个,那么我会在ObjectCache下面看到解决方案。

有多种方法可以解决此问题:

  • 我建议不要使用foreignAutoRefresh = true. 如果你需要Gallery一张照片,那么你可以通过做得到它galleryDao.refresh(photo.getGallery())。这打破了急切的获取链。
  • 你也可以让photos收藏急于求成。延迟加载的集合会更多次访问数据库,但也会破坏循环。
  • 如果您真的必须拥有所有急切的收藏和刷新,那么最好的解决方案是引入一个ObjectCache. 您可能必须经常清除缓存,但是每个 DAO 都会在缓存中查找并返回相同的对象实体,即使在进行急切的 fetch 循环时也是如此。

    galleryDao = getHelper().getRuntimeExceptionDao(Gallery.class);
    galleryDao.setObjectCache(true);
    photoDao = getHelper().getRuntimeExceptionDao(Photo.class);
    photoDao.setObjectCache(true);
    ...
    // if you load a lot of objects into memory, you must clear the cache often
    galleryDao.clearObjectCache();
    photoDao.clearObjectCache();
    
于 2013-07-23T14:20:13.177 回答