[Blog] Hunting duplicate strings in Android heap dumps (#7614)

Adds the blog post **"Hunting duplicate strings in Android heap
dumps"**.

A sample app's heap dump holds 61,752 strings, and about 48,000 of them
are copies of just four values. The post shows how to surface this with
PerfettoSQL (grouping `heap_graph_object_data.value_string`, following
each String to its backing array) and Heapdump Explorer's Duplicate
Strings view, then fixes it at the parser with a pooled `KSerializer`
and verifies the result.

CC @primiano
9 files changed
tree: e4a8960efd2ab202e324180f68a2c01285686f9a
  1. 2026-05-13-perfetto-v55/
  2. 2026-06-03-perfetto-v56/
  3. 2026-07-02-perfetto-v57/
  4. 2026-08-24-perfetto-v58/
  5. 2026-09-01-diagnosing-1gb-viewtreeobserver-leak/
  6. 2026-09-20-hunting-android-memory-leaks/
  7. 2026-09-25-hunting-duplicate-strings/
  8. authors/
  9. README.md
  10. template.md
README.md

Perfetto blog

Source for the posts published at https://perfetto.dev/blog.

This is an orphan branch: it shares no history with main and contains no code. Writing a post never touches the code repository, and post images never land in the main branch's object store.

TL;DR

  • One directory per post, named YYYY-MM-DD-short-title.
  • The markdown goes in post.md; images sit next to it.
  • Open a pull request against the blog branch. No CI runs on it.

Getting set up

Do this from a normal main checkout of Perfetto, not from a standalone clone of this branch. This branch contains no code: the generator that turns these files into a website lives on main, under infra/perfetto.dev/, and it looks for the posts in a blog/ directory at the root of that checkout. On its own, this branch is just markdown with nothing to render it.

So check this branch out as a worktree inside a main checkout:

# From the root of a perfetto checkout that is on main.
git fetch origin blog
git worktree add blog blog

That gives you <perfetto>/blog, which is where the build expects to find the posts. /blog is listed in main's .gitignore, so the worktree does not show up as untracked in the code repo.

If you already have the blog branch checked out somewhere else, git will refuse to check it out twice. Symlink it instead:

ln -s /path/to/your/blog/checkout blog

The build treats the directory as optional: with no blog/ worktree the site still builds, just without a blog. That is deliberate, so a contributor who only touches C++ never has to care that this branch exists -- but it does mean a missing worktree looks like a working build with no posts in it rather than an error.

Adding a post

2026-08-25-chasing-a-40ms-jank/
├── post.md
├── sched-track.png
└── flamegraph.png

post.md starts with required front matter:

---
title: Chasing a 40ms jank down to a 3µs futex
author: @stevegolton
summary: A scroll stutter that no in-process profiler could explain.
---

Body starts here. The title above is the page's `<h1>`; do not repeat it.
Field
titleRendered as the <h1> and the browser title.
authorOne or more GitHub handles, space separated: @a @b. Avatars come from GitHub.
summaryOne or two sentences. Used on the index card, the <meta description> and the Atom feed.

The publish date comes from the directory name — there is no date: field to forget to update.

URLs

The date is dropped from the URL, and images are moved under /blog/media/:

SourcePublished at
2026-08-25-chasing-a-40ms-jank/post.md/blog/chasing-a-40ms-jank
2026-08-25-chasing-a-40ms-jank/sched-track.png/blog/media/chasing-a-40ms-jank/sched-track.png

Two posts must not reduce to the same slug once the date is dropped; the build fails if they do.

Writing

Reference an image the obvious way — a plain relative filename. The build rewrites the path:

![The scheduling track](sched-track.png)

Links to other posts and to the docs use absolute site paths:

[an earlier post](/blog/some-other-post)
[the ftrace docs](/docs/data-sources/ftrace)

Do not use ../ in links; the build rejects them. Everything else is ordinary Perfetto markdown — the same renderer as the docs, so callouts (NOTE:, TIP:, WARNING:), mermaid diagrams, <?tabs> blocks and syntax-highlighted code all work.

Cover images

Every post gets a cover: the card image on the index, and the image shown when the post is shared on X, LinkedIn, Slack etc. (og:image).

Set one with the cover front matter key, naming a PNG next to post.md:

cover: cover.png

It must be a PNG: link previews do not render SVG, so the build rejects anything else. Around 1200x675 (16:9) works well for both the card and the preview.

Without a cover key the build generates one from the title: a flat, abstract, text-free SVG. That is fine for the index card, but as an SVG it cannot be a link preview, so shared links get a text-only card.

NOTE: because the generated image is derived from the title, retitling a post without a cover changes its cover.

Previewing

From the root of the main checkout that holds the worktree (see Getting set up above):

infra/perfetto.dev/run-dev-server

then open http://localhost:8082/blog/ (pass --port if that one is taken). The dev server reads the posts straight out of the blog/ worktree, rebuilds on save and reloads the page for you.

If the blog index comes up empty, the worktree is missing -- the build does not create it for you.