从ResourceHolder说起
引没引我先不下结论,我把分析写给你看,看完之后你心中自有答案。
它是一个接口,有两个重要的方法,如下
/** Pool or cachce for resources */
public interface ResourceHolder {
/**
* Polls for garbage collected objects and disposes associated data.
*
* @return Count of resources in use.
*/
long reclaimReleasedResources();
/** Ignores reference count and disposes any associated resources. */
void destroyAllResources();
}
第一个方法是统计使用中的资源个数,第二个方法是强制释放资源。
都包括哪些资源?请看我下面分析
ResourceHolder有两个实现类,一个是ResourceRegistry
这个类有两个map,一个是 registry 记录已经加载完成的任务,一个 futureRegistry 记录正在加载进行中的任务。
它的 destroyAllResources()是清空已完成的任务和取消正在进行的任务同时移除该任务
重写的 reclaimReleasedResources()这个方法直接返回0
代码如下
/**
* Removes all cache entries. Cancels any in progress futures. cancel does not interrupt work in
* progress. It only prevents the final stage from starting.
*/
@Override
public void destroyAllResources() {
// Log.d("steven","ResourceRegistry destroyAllResources");
synchronized (lock) {
Iterator<Map.Entry<Object, CompletableFuture<T>>> iterator =
futureRegistry.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, CompletableFuture<T>> entry = iterator.next();
iterator.remove();
CompletableFuture<T> futureResource = entry.getValue();
if (!futureResource.isDone()) {
futureResource.cancel(true);
}
}
registry.clear();
}
}
@Override
public long reclaimReleasedResources() {
// Resources held in registry are also held by other ResourceHolders. Return zero for this one
// and do
// counting in the other holders.
return 0;
}
另外一个类是 CleanupRegistry,它的实现则如下
/**
* Polls the {@link ReferenceQueue} for garbage collected objects and runs the associated {@link
* Runnable}
*
* @return count of resources remaining.
*/
@Override
@SuppressWarnings("unchecked") // safe cast from Reference to a CleanupItem
public long reclaimReleasedResources() {
CleanupItem<T> ref = (CleanupItem<T>) referenceQueue.poll();
while (ref != null) {
if (cleanupItemHashSet.contains(ref)) {
ref.run();
cleanupItemHashSet.remove(ref);
}
ref = (CleanupItem<T>) referenceQueue.poll();
}
Log.d("steven","");
return cleanupItemHashSet.size();
}
/** Ignores reference count and releases any associated resources */
@Override
public void destroyAllResources() {
Iterator<CleanupItem<T>> iterator = cleanupItemHashSet.iterator();
while (iterator.hasNext()) {
CleanupItem<T> ref = iterator.next();
iterator.remove();
ref.run();
}
}
它的计算资源引用的实现为计算cleanupItemHashSet元素个数,销毁资源则为 遍历cleanupItemHashSet,然后取出CleanupItem,调用它的run方法,这个其实调用的是Runnable的run方法。
既然知道了实现类,那我们看看哪里用到了它们;
- 第一个ResourceManager,它是一个单例类,它初始化的时候就加了好多ResourceRegistry
private ResourceManager() {
//init once since this singleton
addResourceHolder(textureRegistry);
addResourceHolder(materialRegistry);
addResourceHolder(modelRenderableRegistry);
addViewRenderableRegistry();
addResourceHolder(cameraStreamCleanupRegistry);
addResourceHolder(externalTextureCleanupRegistry);
addResourceHolder(materialCleanupRegistry);
addResourceHolder(renderableInstanceCleanupRegistry);
addResourceHolder(textureCleanupRegistry);
}
那这些ResourceRegistry都是什么时候跟踪资源的呢?我搜索了下引用此类的地方
其中Material.Builder,Renderer,ViewRenderable.Builder里面没有追踪,其他都通过 CleanRegistey.register(T trackedObject, Runnable cleanupCallback) 或者 ResourceRegistry.register(Object id, CompletableFuture<T> futureResource)追踪了。
那什么时候加的cleanupCallback呢?我搜了下这个类出现的地方
由此可见,CameraStream,CameraStreamDepth(自己新加的类,非Sceneform的类,处理深度遮挡的。是CameraStream的副本),ExternalTexture,Material,RenderleInstance,Texture,都有。也就是说这些类在销毁资源时都会执行CleanupCallback,这个类的run方法大同小异
,大概意思就是获取filament的引擎,然后去销毁资源。
CameraStream的
@Override
public void run() {
AndroidPreconditions.checkUiThread();
IEngine engine = EngineInstance.getEngine();
if (engine == null && !engine.isValid()) {
return;
}
if (cameraStreamRenderable != UNINITIALIZED_FILAMENT_RENDERABLE) {
scene.remove(cameraStreamRenderable);
}
engine.destroyIndexBuffer(cameraIndexBuffer);
engine.destroyVertexBuffer(cameraVertexBuffer);
}
ExternalTexture的
@Override
public void run() {
AndroidPreconditions.checkUiThread();
IEngine engine = EngineInstance.getEngine();
if (engine == null || !engine.isValid()) {
return;
}
if (filamentTexture != null) {
engine.destroyTexture(filamentTexture);
}
if (filamentStream != null) {
engine.destroyStream(filamentStream);
}
}
Material的
@Override
public void run() {
AndroidPreconditions.checkUiThread();
if (materialInstance != null) {
materialInstance.dispose();
}
if (materialInternalData != null) {
materialInternalData.release();
}
}
RenderableInstance的
@Override
public void run() {
AndroidPreconditions.checkUiThread();
IEngine engine = EngineInstance.getEngine();
if (engine == null || !engine.isValid()) {
return;
}
RenderableManager renderableManager = engine.getRenderableManager();
if (childEntity != 0) {
renderableManager.destroy(childEntity);
}
if (entity != 0) {
renderableManager.destroy(entity);
}
}
Texture的
@Override
public void run() {
AndroidPreconditions.checkUiThread();
if (textureData != null) {
textureData.release();
}
}
最后一个类的方法很重要,因为通过它我发现了另外一个类SharedReference
/**
* Used for managing memory of shared object using reference counting.
*
* @hide
*/
public abstract class SharedReference {
private int referenceCount = 0;
public void retain() {
referenceCount++;
}
public void release() {
referenceCount--;
dispose();
}
protected abstract void onDispose();
private void dispose() {
if (referenceCount > 0) {
return;
}
onDispose();
}
}
这个类的release用到了引用计数,如果引用数大于0,则不会调用onDispose(),增加引用数的方法retain我也全局搜了下如图
我们看到Material,Texture,ViewRenderable初始化时都调用了此方法,按道理讲应该在销毁时调用release方法,但是我一一找了这三个类,发现viewRenderable的release居然在finalize(这个方法不是立马被调用的)调用的,坑啊,真深。
/** @hide */
void dispose() {
AndroidPreconditions.checkUiThread();
ViewRenderableInternalData viewRenderableData = this.viewRenderableData;
if (viewRenderableData != null) {
viewRenderableData.getRenderView().removeOnViewSizeChangedListener(onViewSizeChangedListener);
viewRenderableData.release();
this.viewRenderableData = null;
}
}
/** @hide */
@Override
protected void finalize() throws Throwable {
Log.d("steven","viewrenderable ...finalize....");
try {
ThreadPools.getMainExecutor().execute(() -> dispose());
} catch (Exception e) {
Log.e(TAG, "Error while Finalizing View Renderable.", e);
} finally {
super.finalize();
}
}
这些资源在创建过程中都被ResourceManager全局监控了。在我们销毁ArSceneview时则分别被调用了
ResourceManager.getInstance().reclaimReleasedResources()和ResourceManager.getInstance().destroyAllResources()来释放资源。