Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 6 additions & 28 deletions DuplicatedBitmapAnalyzer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,12 @@ apply plugin: 'java'
version 1.0

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
}


jar {
manifest {
attributes 'Main-Class': 'com.hprof.bitmap.Main'
attributes 'Manifest-Version': version
}
implementation fileTree(include: ['*.aar'], dir: 'libs')
implementation 'com.squareup.haha:haha:2.0.4'
implementation 'com.alibaba:fastjson:1.2.54'
// https://mvnrepository.com/artifact/com.squareup.leakcanary/leakcanary-watcher
implementation group: 'com.squareup.leakcanary', name: 'leakcanary-watcher', version: '1.6.2'

from {
exclude 'META-INF/MANIFEST.MF'
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
exclude 'META-INF/*.RSA'
configurations.runtime.resolve().collect {
it.isDirectory() ? it : zipTree(it)
}
}
}

// copy the jar to work directory
task buildAlloctrackJar(type: Copy, dependsOn: [build, jar]) {
group = "buildTool"
from('build/libs') {
include '*.jar'
exclude '*-javadoc.jar'
exclude '*-sources.jar'
}
into(rootProject.file("tools"))
}

Binary file added DuplicatedBitmapAnalyzer/libs/classes.jar
Binary file not shown.
133 changes: 133 additions & 0 deletions DuplicatedBitmapAnalyzer/src/com/hprof/bitmap/Analyzer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.hprof.bitmap;

import com.alibaba.fastjson.JSON;
import com.squareup.haha.perflib.*;
import com.squareup.haha.perflib.io.HprofBuffer;
import com.squareup.haha.perflib.io.MemoryMappedFileBuffer;
import jdk.nashorn.internal.ir.WhileNode;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Analyzer {
private HashMap<ArrayInstance, Instance> byteArrayToBitmapMap = new HashMap<>();
Set<ArrayInstance> byteArrays = new HashSet<>();
private Snapshot snapshot;
ShortestPathFinder shortestPathFinder = new ShortestPathFinder(null);
private List<DuplicateResult> duplicateResults = new ArrayList<>();

private HashMap<ArrayInstance, Object[]> cacheValuesMap = new HashMap<>();

public void find() throws IOException {
System.out.println();
File heapFile = new File(System.getProperty("user.dir") + "\\DuplicatedBitmapAnalyzer\\res\\hprof\\1.hprof");
HprofBuffer hprofBuffer = new MemoryMappedFileBuffer(heapFile);
HprofParser parser = new HprofParser(hprofBuffer);
snapshot = parser.parse();
ClassObj bitmapClass = snapshot.findClass("android.graphics.Bitmap");
List<Instance> bitmapInstances = getBitmapInstances(snapshot, bitmapClass);
bitmapInstances.stream().forEach(instance -> {
ArrayInstance arrayInstance = HahaHelper.fieldValue(((ClassInstance) instance).getValues(), "mBuffer");
byteArrayToBitmapMap.put(arrayInstance, instance);

});
byteArrays.addAll(byteArrayToBitmapMap.keySet());


// 缓存Bitmap的数组
cacheValuesMap = new HashMap<>(byteArrays.size());
byteArrays.forEach(arrayInstance -> {
cacheValuesMap.put(arrayInstance, arrayInstance.getValues());
});

// 根据Bitmap数组长度进行分类
Map<Integer, Set<ArrayInstance>> arrayInstanceMapBySize =
byteArrays.stream().collect(Collectors.groupingBy(ArrayInstance::getSize,
Collectors.mapping(Function.identity(), Collectors.toSet())));

// bitmap数组大小不同的判定为非重复图片,移除
arrayInstanceMapBySize.keySet().stream()
.filter(key -> arrayInstanceMapBySize.get(key).size() > 1)
.forEach(size -> compareBitmapIsSame(arrayInstanceMapBySize.get(size), size));
}


/*
* 通过比较两个Bitmap的hashcode
*/
private void compareBitmapIsSame(Set<ArrayInstance> bitmaps, int bitmapArrayLength) {
Map<Object, Set<ArrayInstance>> prefixMap = new HashMap<>();

for (int column = 0; column < bitmapArrayLength; column++) {
prefixMap.clear();
for (ArrayInstance arrayInstance : bitmaps) {
Object[] bitmapArray = cacheValuesMap.get(arrayInstance);

if (prefixMap.containsKey(bitmapArray[column])) {
prefixMap.get(bitmapArray[column]).add(arrayInstance);
} else {
Set<ArrayInstance> prefixBitmapArrays = new HashSet<>();
prefixBitmapArrays.add(arrayInstance);
prefixMap.put(bitmapArray[column], prefixBitmapArrays);
}
}

// 每次移除
prefixMap.forEach((key, value) -> {
if (value.size() < 2) {
bitmaps.remove(value.toArray()[0]);
}
});
}

prefixMap.forEach((key, arrayInstances) -> arrayInstances.forEach(arrayInstance -> {
if (arrayInstances.size() > 1) {
duplicateResults.add(getDuplicateResult(byteArrayToBitmapMap.get(arrayInstance), arrayInstances.size()));
}
}));
System.out.println(JSON.toJSONString(duplicateResults));
}

/*
* art虚拟机堆存储区域分为default、app、image、zygote, 这里只分析在app heap中的bitmap
* 整个过程:先拿到Bitmap的类对象,根据类对象获取到堆里面的对象实例
*/
private List<Instance> getBitmapInstances(Snapshot snapshot, ClassObj bitmapClass) {
List<Instance> reachableInstances = new ArrayList<>();
snapshot.getHeaps()
.stream()
.filter(heap -> "app".equals(heap.getName()))
.forEach(heap -> bitmapClass
.getHeapInstances(heap.getId())
.stream()
// .filter(instance -> instance.getDistanceToGcRoot() != Integer.MAX_VALUE)
.forEach(instance -> {
reachableInstances.add(instance);
}));
return reachableInstances;
}

/**
* 打印堆栈信息
*/
private DuplicateResult getDuplicateResult(Instance instance, int size) {
DuplicateResult duplicateResult = new DuplicateResult();
duplicateResult.setDuplcateCount(size);
duplicateResult.setHeight(HahaHelper.fieldValue(((ClassInstance) instance).getValues(), "mHeight"));
duplicateResult.setWidth(HahaHelper.fieldValue(((ClassInstance) instance).getValues(), "mWidth"));
ShortestPathFinder.Result result = shortestPathFinder.findPath(snapshot, instance);
LeakNode leakNode = result.leakingNode;
List<String> stackInfo = new ArrayList<>();
while (leakNode != null) {
stackInfo.add(leakNode.instance.toString());
leakNode = leakNode.parent;
}
duplicateResult.setStack(stackInfo);
duplicateResult.setBufferHash(instance.hashCode());
duplicateResult.getBufferSize(instance.getSize());
return duplicateResult;
}
}
65 changes: 65 additions & 0 deletions DuplicatedBitmapAnalyzer/src/com/hprof/bitmap/DuplicateResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.hprof.bitmap;

import java.util.List;

public class DuplicateResult {
private int duplcateCount;
private List<String> stack;
private int bufferHash;
private int width;
private int height;
private long bufferSize;

public int getDuplcateCount() {
return duplcateCount;
}

public void setDuplcateCount(int duplcateCount) {
this.duplcateCount = duplcateCount;
}

public List<String> getStack() {
return stack;
}

public void setStack(List<String> stack) {
this.stack = stack;
}

public int getBufferHash() {
return bufferHash;
}

public void setBufferHash(int bufferHash) {
this.bufferHash = bufferHash;
}

public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}

public int getHeight() {
return height;
}

public void setHeight(int height) {
this.height = height;
}

public long getBufferSize(int size) {
return bufferSize;
}

public void setBufferSize(long bufferSize) {
this.bufferSize = bufferSize;
}

@Override
public String toString() {
return super.toString();
}
}
Loading