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
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
buildscript {
ext.kotlin_version = '1.3.11'

repositories {
google()
jcenter()
Expand All @@ -11,7 +13,7 @@ buildscript {

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }
}

allprojects {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.ld.hprofanalyze

import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals("com.ld.hprofanalyze", appContext.packageName)
}
}
149 changes: 149 additions & 0 deletions hprofanalyze/src/main/java/com/ld/hprofanalyze/TestActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.ld.hprofanalyze

import android.Manifest
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Debug
import android.os.Environment
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import com.squareup.haha.perflib.io.MemoryMappedFileBuffer
import kotlinx.android.synthetic.main.activity_test.*
import java.io.File
import java.util.*
import kotlin.collections.ArrayList
import android.util.Log
import com.squareup.haha.perflib.*
import java.lang.StringBuilder
import java.lang.reflect.Array


private const val WRITE_EXTERNAL_STORAGE_REQUEST_CODE = 100
private const val TAG = "TestActivity"

class TestActivity : AppCompatActivity() {
private var externalReportPath: File? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
WRITE_EXTERNAL_STORAGE_REQUEST_CODE)
} else {
initExternalReportPath()
afterPermissionGrant()

}

}

override fun onRequestPermissionsResult(
requestCode: Int, permissions: kotlin.Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
initExternalReportPath()
afterPermissionGrant()
}

private fun initExternalReportPath() {
externalReportPath = File(Environment.getExternalStorageDirectory(), "hprofdir")
if (externalReportPath?.exists() ?: false) {
externalReportPath?.mkdirs()
}

}

private fun afterPermissionGrant() {
// 1、构造内存泄漏(重复加载同一张图片)
var bitmap1 = BitmapFactory.decodeResource(resources, R.drawable.test)
image1.setImageBitmap(bitmap1)
var bitmap2 = BitmapFactory.decodeResource(resources, R.drawable.test)
image2.setImageBitmap(bitmap2)
var bitmap3 = BitmapFactory.decodeResource(resources, R.drawable.test)
image3.setImageBitmap(bitmap3)
var bitmap4 = BitmapFactory.decodeResource(resources, R.drawable.test)
image4.setImageBitmap(bitmap4)
dump.setOnClickListener {
Debug.dumpHprofData(externalReportPath?.absolutePath)
}
parse.setOnClickListener {
// 2、Haha库使用,生成内存快照。
var memoryMappedFileBuffer = MemoryMappedFileBuffer(externalReportPath)
var hprofParser = HprofParser(memoryMappedFileBuffer)
var snapshot = hprofParser.parse()
snapshot.computeDominators()
var heaps = snapshot.heaps
heaps = heaps.filter {
it.name.equals("app") || it.name.equals("default")
}
// 3、获取bitmap相关的对象的内存快照。
var bitmapClass = snapshot.findClass("android.graphics.Bitmap")
var bitmaplist = arrayListOf<Instance>()
heaps.forEach {
bitmaplist.addAll(bitmapClass.getHeapInstances(it.id))
}
var bitmapMapToBuffer = mutableMapOf<Instance, ArrayInstance>()
// 4、构建map key:bitmap内存映射对象,value:bitmap Class 的mbuffer字段值
bitmaplist.forEach {
(it as ClassInstance).values.forEach { buffer ->
if (buffer.field.name.equals("mBuffer")) {
bitmapMapToBuffer[it] = buffer.value as ArrayInstance
}
}
}
// 5、构建map key:步骤4中的mbuffer字段的数组的hashcode,value:拥有相同hashcode的Instance 集合。
var result = mutableMapOf<Int, ArrayList<Instance>>()
bitmapMapToBuffer.forEach { instance, byteBuffer ->
var hashCode = Arrays.hashCode(byteBuffer.values)
if (result.containsKey(hashCode)) {
result[hashCode]?.add(instance)
} else {
result[hashCode] = arrayListOf(instance)
}
}
// 6、过滤步骤5中map,如果value(list)的size大于1,就表示有多个instance的mbuffer是一样的,就可以被认为是内存泄漏了。
var filterResult = result.filter {
it.component2().size > 1
}
Log.d(TAG, "发现重复bitmap 数量为 ${filterResult.size}")
// 7、获取bitmap的宽高和buffer长度
filterResult.forEach { hashcode, instances ->
var leak = LeakObjectDetaial()
instances.forEach {
(it as ClassInstance).values.forEach { fieldValue ->
leak.leakCount++
when (fieldValue.field.name) {
"mWidth" -> leak.width = fieldValue.value as Int
"mHeight" -> leak.height = fieldValue.value as Int
"mBuffer" -> leak.bufferSize = (fieldValue.value as ArrayInstance).values.size
}
}
// 8、获取bitmap的引用链。
var stackFrame = StringBuilder()
stackFrame.append(it.classObj.className).append("\n")
var nextInstanceToGcRoot = it.nextInstanceToGcRoot
while (nextInstanceToGcRoot != null) {
nextInstanceToGcRoot.classObj
stackFrame.insert(0, nextInstanceToGcRoot.classObj.className + "\n")
nextInstanceToGcRoot = nextInstanceToGcRoot.nextInstanceToGcRoot
}
leak.stacks = stackFrame.toString()
Log.d(TAG, "width:${leak.width},height:${leak.height},buffersize:${leak.bufferSize},stack:${leak.stacks}")
}
}
}
}

class LeakObjectDetaial {
var leakCount: Int = 0
var stacks: String = ""
var width: Int = 0
var height: Int = 0
var bufferSize: Int = 0
}

}
Binary file added hprofanalyze/src/main/res/drawable/test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions hprofanalyze/src/main/res/layout/activity_main2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Main2Activity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
68 changes: 68 additions & 0 deletions hprofanalyze/src/main/res/layout/activity_test.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TestActivity">


<ImageView
android:id="@+id/image1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/image2"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
android:id="@+id/image2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/image1"
app:layout_constraintRight_toLeftOf="@id/image3"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
android:id="@+id/image3"
android:layout_width="0dp"

android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/image2"
app:layout_constraintRight_toLeftOf="@id/image4"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
android:id="@+id/image4"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@id/image3"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />


<Button
android:id="@+id/dump"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DumpHprof文件"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.2" />

<Button
android:id="@+id/parse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="分析Hprof文件"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.8" />


</android.support.constraint.ConstraintLayout>
17 changes: 17 additions & 0 deletions hprofanalyze/src/test/java/com/ld/hprofanalyze/ExampleUnitTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.ld.hprofanalyze

import org.junit.Test

import org.junit.Assert.*

/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1 +1 @@
include ':DuplicatedBitmapAnalyzer'
include ':DuplicatedBitmapAnalyzer', ':hprofanalyze'