Skip to content

Instantly share code, notes, and snippets.

@fourlastor
Created June 5, 2024 13:58
Show Gist options
  • Save fourlastor/23602bb72a7e2781dc88cd05dfe14281 to your computer and use it in GitHub Desktop.
Save fourlastor/23602bb72a7e2781dc88cd05dfe14281 to your computer and use it in GitHub Desktop.
leak canary JVM
dependencies {
implementation("com.squareup.leakcanary:leakcanary-object-watcher-android-support-fragments:2.12")
implementation("com.squareup.leakcanary:shark:2.12")
}
import com.sun.management.HotSpotDiagnosticMXBean
import java.lang.management.ManagementFactory
object HotSpotHeapDumper {
private val mBean: HotSpotDiagnosticMXBean by lazy {
val server = ManagementFactory.getPlatformMBeanServer()
ManagementFactory.newPlatformMXBeanProxy(
server,
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean::class.java
)
}
fun dumpHeap(fileName: String) {
mBean.dumpHeap(fileName, LIVE)
}
private const val LIVE = true
}
import leakcanary.GcTrigger
import leakcanary.ObjectWatcher
import leakcanary.OnObjectRetainedListener
import shark.HeapAnalyzer
import shark.KeyedWeakReferenceFinder
import shark.ObjectInspectors
import shark.OnAnalysisProgressListener
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale.US
class JvmHeapAnalyzer(private val objectWatcher: ObjectWatcher) :
OnObjectRetainedListener {
private val fileNameFormat = SimpleDateFormat(DATE_PATTERN, US)
override fun onObjectRetained() {
GcTrigger.Default.runGc()
if (objectWatcher.retainedObjectCount == 0) {
return
}
val fileName = fileNameFormat.format(Date())
val hprofFile = File(fileName)
println("Dumping the heap to ${hprofFile.absolutePath}")
HotSpotHeapDumper.dumpHeap(hprofFile.absolutePath)
val analyzer = HeapAnalyzer(
OnAnalysisProgressListener { step ->
println("Analysis in progress, working on: ${step.name}")
})
val heapDumpAnalysis = analyzer.analyze(
heapDumpFile = hprofFile,
leakingObjectFinder = KeyedWeakReferenceFinder,
computeRetainedHeapSize = true,
objectInspectors = ObjectInspectors.jdkDefaults
)
println(heapDumpAnalysis)
}
companion object {
private const val DATE_PATTERN = "yyyy-MM-dd_HH-mm-ss_SSS'.hprof'"
}
}
class JvmLeakTracer : LeakTracer {
private val scheduledExecutor = Executors.newSingleThreadScheduledExecutor()
private val objectWatcher = ObjectWatcher(
clock = { System.currentTimeMillis() },
checkRetainedExecutor = { command -> scheduledExecutor.schedule(command, 5, TimeUnit.SECONDS) }
).apply {
addOnObjectRetainedListener(JvmHeapAnalyzer(this))
}
override fun expectWeaklyReachable(watchedObject: Any, description: String) {
objectWatcher.expectWeaklyReachable(watchedObject, description)
}
}
/** Memory leak tracer. */
interface LeakTracer {
/** Call this method when you expect [watchedObject] to be released from memory. */
fun expectWeaklyReachable(watchedObject: Any, description: String)
object NoOp : LeakTracer {
override fun expectWeaklyReachable(watchedObject: Any, description: String) = Unit
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment