Keep composition cheap and rare: give lazy lists a key and a contentType, cache expensive work with remember, read fast-changing state as late and as low in the tree as possible, and judge the result only on a release build.
Lazy lists: items(list, key = { it.id }, contentType = { it.type }), so rows keep their state and slots are reused between rows of the same shape.
**remember heavy work**: sorting, filtering, formatters, Brush and Path objects go into remember(keys) { } or into the ViewModel, not into the composable body.
**derivedStateOf: when state changes far more often than the value you show, such as a scroll index driving a "scroll to top" button.
Deferred reads**: for values that change every frame, pass () -> T or use the lambda modifiers offset { }, graphicsLayer { }, drawBehind { }.
Stable inputs: immutable UI models, ImmutableList, and no new list or object built inside composition and handed to a child.
No backwards writes: never write to state that was already read in the same composition.
Measure in release: R8 on and a baseline profile installed; a debug build is far slower and lies about jank.
AndroidSenior
Compose Performance & Stability
Stability, skipping, deferred reads, phases, Lazy keys, Modifier.Node, compiler metrics
Questions closed
0 / 19
- 0Review
- 5Asked often
Questions
A debug build runs the Compose runtime without R8 and with most ART optimizations switched off, so it can be several times slower than what users get; jank measured there may not exist in release at all.
No R8: in release, R8 shrinks and optimizes the Compose libraries together with your code: inlining, dead-branch removal, class merging. Compose is a big library that ships inside your APK, so it gains more from this than the View system does.
**debuggable turns off ART optimizations: the runtime keeps code debuggable instead of fast, and a baseline profile does not help such a build.
Debug-only extras: source information for the Layout Inspector and hooks for Live Edit add work to every composition.
What to profile**: a build type made from release (initWith), signed with the debug key and not debuggable, plus <profileable android:shell="true" /> in the manifest so Studio and Perfetto can attach. The androidx.baselineprofile plugin creates a benchmarkRelease variant of exactly this shape.
Recomposition counts are the exception: they need a debuggable build, and counts, unlike timings, stay valid there.
renders++ is a backwards write: it changes state that was already read earlier in the same composition. The write invalidates the scope, the next frame recomposes it, which increments again, so Feed recomposes on every frame forever.
Why it loops: Compose re-runs a scope when state it read changes. Writing after the read schedules another recomposition of the same scope, and the value is always different, so it never settles.
How it shows up: a recomposition counter in the Layout Inspector that never stops ticking, a busy main thread in a trace, battery drain on a screen that looks idle.
A related loop: "syncing" a parameter into state from the body (if (x != local) local = x) is the same backwards write; writing a size from onSizeChanged into state that the same composable uses for its own layout is a feedback loop of the same shape: at best one frame late, at worst it oscillates.
The rule: composition must be a pure function of its inputs. Writes belong in event handlers, in effects, or in the ViewModel.
The fix: derive the value instead of syncing it, and count recompositions with a plain holder updated in SideEffect, which is not snapshot state and does not trigger anything.
bannerOffset is read in the body of ChatScreen (the content of Box is inline), so every animation frame invalidates the whole screen's composition. The lambda overload offset { } moves the read into the layout phase: frames only re-place the banner, with no recomposition.
Why the whole screen: a state read subscribes the phase it happens in. Modifier.offset(y = bannerOffset) evaluates the value while composing, inside ChatScreen's restart scope, so each new Dp re-runs that scope, rebuilds the modifier chain and recomposes NetworkBanner. MessageList survives only because the same messages instance lets it skip.
How the fix works: offset { } takes a Density.() -> IntOffset that runs during placement. Through the by delegate, bannerOffset inside the lambda calls State.value at layout time, so the change reaches layout and draw and composition never hears of it. roundToPx() is available because the lambda has a Density receiver.
Cheaper still: graphicsLayer { } defers the read to the draw phase, so a pure move skips re-placement too.
Catching it: the foundation lint check UseOfNonLambdaOffsetOverload flags offset(x, y) fed from a State; in Layout Inspector the recomposition count of ChatScreen climbs during the animation instead of staying flat.
remember without keys runs its block once, and derivedStateOf re-evaluates only when snapshot state read inside it changes. contacts and query are plain parameters captured on the first composition, so the list stays filtered by the first query forever.
Why it goes stale: the lambda closes over the values of the first call. Later compositions bring a new query, but remember hands back the old State, whose calculation still sees the old values and has no reason to rerun: it recorded no state reads at all.
How the fix works: the inputs become keys. remember(contacts, query) { } refilters exactly when either of them changes and returns the cached list otherwise, which is the caching the comment was after.
**When derivedStateOf is right**: when it reads State objects and its result changes less often than they do: listState.firstVisibleItemIndex > 0 driving a "scroll to top" button. Here the result changes with every keystroke, so it would only add a snapshot object and dependency tracking.
For a big list: move the filter into the ViewModel (combine(contacts, query) with flowOn(Dispatchers.Default)), so typing never filters on the main thread.
Catching it: a UI test that types into the search field and asserts the row count; in review, a key-less remember { } whose block mentions a parameter is a red flag.
key controls identity; contentType controls reuse. Without it every row is a candidate for every recycled slot, so a slot that held a text row gets handed to a photo row and the runtime throws the whole subtree away instead of updating it.
What to give the list
items(posts, key = { it.id }, contentType = { it.kind }) on every call. The key keeps state and item animations attached to the right row; the content type groups rows whose composition can actually be reused.
Prefer the items / itemsIndexed overloads over a repeat inside a single item { }. One item is one slot: the whole block composes at once and nothing is virtualized.
Never put a LazyColumn inside Column(Modifier.verticalScroll()): it gets an unbounded height and throws; use one lazy list and turn the surrounding content into item { } calls.
Keep the row body cheap. No BoxWithConstraints (it is a SubcomposeLayout, so it composes its content during measurement), no image decode on the main thread, no SimpleDateFormat per row; hoist formatting into the model.
Modifier.animateItem(), which replaced animateItemPlacement() in Foundation 1.7, needs the key and costs an animation per visible item. Add it where it reads, not everywhere.
Rule of thumb: one lazy list per axis, a key and a contentType on every items call, and nothing in a row body you would be unhappy to run sixty times a second.
A recomposition count is how many times that restart scope actually re-ran, and 300 on a static header means something invalidated it 300 times; the Skips column beside it tells you whether the runtime managed to bail out or not.
Reading the two columns
Recompositions: the scope re-ran its body. Skips means the parent re-ran and called it with equal parameters, so the runtime skipped the body. Many skips and few recompositions is the healthy shape; the number to be alarmed by is recompositions climbing on a composable whose inputs did not change.
Counts attach to restartable scopes only. Column, Row and other inline composables have none, so their work shows up on the nearest restartable ancestor.
Reset the counters, perform exactly one interaction, then read the delta. The total since launch tells you nothing.
It requires a debuggable build with source information, so trust the counts and ignore the timings from that build.
Getting from the count to the cause
Walk up the tree. If the parent's count matches the header's, the invalidation comes from above and the parent is the composable that could not skip.
If the count is the header's own, it reads snapshot state directly: a collectAsStateWithLifecycle, a scroll position, an animation value read in the body.
Check the compiler report for that function: restartable without skippable, or a parameter marked unstable that is a new instance each frame, explains most cases.
Fix by pushing the read down into the leaf that needs it, deferring it into a lambda, or hoisting the allocation out of composition, then reset and measure again.
The counts are a compass, not a verdict: a high count on a trivial composable costs nothing measurable, and a janky frame with zero recompositions means the problem is in layout or draw.
Switch on the compiler's reports and metrics in the composeCompiler block, build the variant you ship, and read <module>-composables.txt, which prints every composable with a restartable/skippable header and each parameter marked stable or unstable.
Turning them on
The Compose compiler ships inside Kotlin since 2.0 as org.jetbrains.kotlin.plugin.compose, so it is configured through the composeCompiler { } Gradle extension (reportsDestination and metricsDestination), not the old kotlinCompilerExtensionVersion.
Output is written per module and per variant at compile time. Gate it behind a Gradle property so ordinary builds and CI do not pay for it, and always read the variant you actually ship.
What the four files say
<module>-composables.txt is the human-readable one: restartable skippable fun PostRow(stable post: Post, unstable tags: List<Tag>). An unstable parameter is the first suspect; with strong skipping it no longer blocks skipping but is compared by ===.
<module>-classes.txt: stable class / unstable class per type, with the offending field named. This is where you learn that one var or one List poisoned a whole model.
<module>-module.json: aggregate counts. Track knownUnstableArguments between releases; a rise is a regression.
<module>-composables.csv: the same rows, for scripting a diff.
What the report cannot tell you
It is static analysis. It says a function can skip, never how often it did: a skippable composable still re-runs when a parameter's identity changes every frame, so pair it with recomposition counts.
Anything from a module compiled without the plugin is reported unstable. That is a build-setup fact, not a modeling mistake.
Inline composables such as Column and Row never appear; they have no restart scope of their own.
A type is stable when Compose can trust equals to say whether it changed and any change to a public property is reported through snapshot state; @Immutable promises nothing ever changes, @Stable promises changes go through snapshot state.
Inferred stable without annotations
Primitives, String, enums, function types and the kotlinx.collections.immutable types.
A class whose public properties are all vals of stable types. One public var of a plain type makes the whole class unstable.
Generic classes are stable conditionally: Page<T> is stable only when T is.
A type from a module compiled without the Compose compiler (a pure Kotlin or Java library) gets no inference and counts as unstable.
Why List<Tag> is unstable
List is a read-only interface, not an immutable type: the object behind it may be an ArrayList that someone mutates in place. Use ImmutableList and convert with toImmutableList() where the UI model is built.
The annotations
@Immutable: every public property is fixed after construction. The usual choice for UI models.
@Stable: properties may change, but only through snapshot state such as mutableStateOf, and equals gives the same answer for the same two instances.
Nothing checks either promise. Annotate a class that mutates silently and the screen stops updating: a worse bug than an extra recomposition.
Since strong skipping, an unstable parameter no longer prevents skipping; it is compared by identity (===) instead of equals, so a freshly built but equal object still recomposes the child.
Strong skipping makes every restartable composable skippable by comparing unstable parameters with instance identity instead of refusing to skip at all, and it memoizes lambdas for you.
What changed
Before, one unstable parameter made a composable non-skippable, so it re-ran whenever its parent ran. Now it is restartable skippable in the report regardless.
Stable parameters are still compared with equals; unstable ones with ===.
Lambdas declared inside composables are memoized automatically, so the remember { { ... } } wrapper that older articles insist on is noise for a plain callback.
Opt a single function out with @NonSkippableComposable, or turn the mode off for the module with featureFlags.add(ComposeFeatureFlag.StrongSkipping.disabled()) in the composeCompiler block.
What it does not rescue
A new instance every recomposition. ArticleCard(tags = article.tags.map(::toTag)) builds a fresh list each time, identity never matches, and the card recomposes forever. Move the mapping out of composition.
A state read too high in the tree. Skipping cannot save a composable that its own read invalidates.
Non-restartable composables. Inline lambdas (Column, Row, Box content) and composables that return a value have no restart scope, so there is no place to skip.
Expensive bodies. Formatting a date, sorting a list or decoding an image still costs what it costs on the runs that do happen.
Layout and draw. Skipping is a composition-phase optimization and does nothing for a slow measure or an overdrawn frame.
The mental model: skipping decides whether a function re-runs. Most of the milliseconds people are chasing come from what it does when it does run, and from the two phases after it.
Strong skipping memoizes the lambda, but keyed on what it captures: progress is new every frame, so the lambda is a new instance every frame and Controls sees a changed parameter. Capture a State holder instead of the value and read the value when the lambda runs.
Why memoization does not help
The compiler rewrites { onSeek(progress + 10f) } into roughly remember(onSeek, progress) { ... }: stable captures compared with equals, unstable ones with ===. A new progress means a new lambda and no skip.
Wrapping it yourself in remember { } with no keys fixes the identity and breaks the callback: it keeps the first progress forever, and the button seeks from a position long gone.
The fix
rememberUpdatedState(progress) keeps one State object for the life of the call site and writes the newest value into it on each composition. The lambda captures that State, whose identity never changes, and reads .value when it is invoked.
Read .value inside the lambda, never in the body; a read in the body subscribes the composition again.
Often cleaner: do not route a moving value through the callback at all. onSkip = viewModel::skipForward lets the ViewModel use the position it already owns.
Rule of thumb: a callback should capture identities, not values that move every frame.
It rewrites every composable into ordinary code that takes a hidden $composer and a $changed bitmask, wraps the body in groups recorded in the slot table, and adds the skip and restart logic; that is what lets Compose remember values by position and re-run only part of the tree.
The rewrite
**$composer: an extra parameter threaded through every composable call. It is the cursor into the slot table, and the reason a composable can only be called from another composable.
$changed**: an Int bitmask with a few bits per parameter, telling the callee what the caller already knows: same, different, static or uncertain. A child does not re-compare a value the parent already proved unchanged.
Groups: the body is wrapped in startRestartGroup / endRestartGroup; if branches and key() get groups of their own. Group keys come from the source location, which is what positional memoization means.
Skip and restart: a skippable function starts with "nothing changed and $composer.skipping → skipToGroupEnd()". At the end it registers an updateScope { } lambda that calls the function again with the same arguments, so Compose can recompose just this function later.
The slot table
A flat array structure (historically a gap buffer) that stores groups and their slots in execution order: remember values, state objects, parameters, references to nodes.
On recomposition the runtime walks it in the same order, and remember returns whatever sits at the current position. That is why a remember inside a loop hands back the wrong value after a reorder unless it is wrapped in key().
Changes are collected into a change list and applied to the LayoutNode tree by an Applier afterwards.
What you meet in practice
Inline composables and composables that return a value have no restart group, so they cannot restart or skip on their own.
@ReadOnlyComposable drops group generation for functions that only read, like MaterialTheme.colorScheme; @NonRestartableComposable drops the restart group for tiny wrappers.
State in Compose is identified by position in the composition, so calling the same composable from a different call site creates a different group: the old one is disposed and a new one built from scratch. movableContentOf is the API that relocates a group instead.
Why position is identity
The slot table keys every group by its call site, plus whatever key() you supplied. if (wide) Row { Player() } else Column { Player() } is two call sites; leaving one disposes its remembers, cancels its LaunchedEffects and destroys its LayoutNodes.
rememberSaveable rescues plain values, not a running effect, an ExoPlayer instance, a scroll position object or an animation in flight.
What movableContentOf does
remember { movableContentOf { ... } } wraps the content in a callable whose group the runtime is allowed to move. When it disappears from one call site and reappears at another within the same composition pass, the existing slots and nodes are moved over: state, effects and node identity all survive.
Remove it in one pass and add it in the next and there is nothing left to move: the group has already been discarded and you get a fresh one.
movableContentOf lives inside one composition. A rotation that recreates the Activity destroys it; keep the player in a ViewModel or handle android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" yourself.
Invoking the same movable content from two places at once gives two independent copies, not one shared instance.
Use the parameterized overloads (movableContentOf<T>) instead of capturing arguments, or a keyless remember freezes them.
key() is the sibling-level version
key(id) { Row(...) } fixes identity among siblings of one parent, so a reorder or an insertion keeps each child's state; that is exactly what LazyColumn's key = { it.id } does. It cannot move anything between parents.
Rule of thumb: key for reordering inside one parent, movableContentOf for a subtree that changes parents, LookaheadScope on top when you want that move animated rather than instant.
A composed { } modifier is a composable that runs once per usage site on every composition, and because two composed instances never compare equal it also defeats the equality check the modifier chain would otherwise use to skip work.
What composed costs
The factory is materialized during composition, so each usage becomes a composition group with its own slots: one per element, per call site, per recomposition. A hundred rows means a hundred little compositions.
The resulting modifier has no meaningful equals, so it cannot be hoisted into a top-level val and reused, and a parent that would have skipped now sees a changed modifier parameter.
A state read in the factory body (not in a draw or layout lambda) invalidates composition, even when only drawing had to change. An animation written this way recomposes the whole subtree at 60 Hz.
What a node gives you instead
You split it in two: a ModifierNodeElement<T> data class holding the parameters, with equals/hashCode for free, and a Modifier.Node holding the behavior and its mutable state.
On recomposition the runtime diffs elements. Equal means nothing happens at all; different means update(node) mutates the existing node: no allocation, no group, no composition.
The node implements only the interfaces it needs (DrawModifierNode, LayoutModifierNode, PointerInputModifierNode, SemanticsModifierNode) and invalidates exactly one phase with invalidateDraw(), invalidateMeasurement() or invalidatePlacement().
It has a real lifecycle (onAttach/onDetach) and its own coroutineScope, so an animation can run entirely inside the node and never touch the composition. currentValueOf(LocalContentColor) reads a CompositionLocal without subscribing a composable to it.
composed still exists but the framework has moved all of its own modifiers off it. The mental model: composed puts your modifier in the composition, a node puts it in the layout tree; anything that reacts per frame belongs in the tree, where invalidating draw alone is possible.
BoxWithConstraints is a SubcomposeLayout: it moves the composition of its children into the measure pass, so each row now composes during layout, every time it is measured, instead of once during composition.
What subcomposition costs
The content cannot be composed until constraints exist, so the runtime runs a nested composition inside the layout phase. That work is not batched with the regular composition pass, and it runs again every time the item is re-measured with new constraints.
Each SubcomposeLayout carries its own SubcomposeLayoutState with slot bookkeeping and a reuse policy. A hundred of them in a scrolling list is a hundred separate little compositions being created and thrown away.
Intrinsics are not supported at all: a parent that asks a SubcomposeLayout for minIntrinsicHeight, such as Row(Modifier.height(IntrinsicSize.Min)), gets an IllegalStateException, because the content does not exist until it is measured.
A state read inside the block invalidates layout and the subcomposition, so a size-driven animation recomposes on every frame.
What to use instead
If constraints only change how you measure, not what you compose, use Modifier.layout { measurable, constraints -> } or a custom Layout. No subcomposition at all.
If a parent must size itself to its largest child, IntrinsicSize.Min / Max costs one extra measure pass and zero extra composition.
Read the breakpoint once, high up (one BoxWithConstraints around the whole list, or currentWindowAdaptiveInfo()), and pass a boolean down.
LazyLayout is subcomposition used correctly: compose only what is visible and reuse the slots. Reach for SubcomposeLayout when the content itself depends on the constraints; if only the numbers do, it is a Layout.
Three real options: declare the class stable in a stability configuration file, map it into a type you own, or apply the Compose compiler to the module that produces it; mapping into a type you own is the only one that needs neither an unchecked promise nor control of the SDK module.
Why it is unstable in the first place
The compiler infers stability from a class's fields, but across a module boundary it can only do that from metadata the Compose compiler plugin writes. A class from a plain Kotlin or Java artifact carries none, so it is unstable even if every field is a val.
List, Map and Set are interfaces that could be backed by a mutable implementation, so a single List<Post> field makes the whole class unstable too.
The three options
Stability configuration file: composeCompiler { stabilityConfigurationFiles.add(...) }, one fully-qualified name per line, * wildcards allowed. Zero code. It is an unchecked promise: name a class that really does mutate in place and Compose skips a recomposition that was needed, and you debug stale UI rather than a crash.
A UI model you own: map the SDK type at the ViewModel boundary into your own data class with ImmutableList fields from kotlinx.collections.immutable. Costs mapping code, buys a UI layer that does not break when their schema changes. This is the answer for anything you did not write.
Apply the Compose compiler plugin to the module: works when the offending module is yours; the plugin emits the metadata and stability is inferred. A module that must stay free of Compose can depend on just androidx.compose.runtime:runtime-annotation and mark its classes @Immutable.
What strong skipping changed about the answer
Unstable parameters no longer block skipping at all; they are compared by instance identity instead of equals. So an unstable type only hurts when the producer hands out a new instance on every emission. Check that first: a repository that re-emits the same cached object skips perfectly well while unstable.
Annotate what you can prove, wrap what you cannot.
The first pass is cold: ART interprets code that was never compiled, classes load on the frame thread, and the slot table, layout nodes and caches are built from scratch. A Baseline Profile fixes the code part; the rest needs fewer distinct item layouts.
What is cold, in order
Code. A fresh install has no AOT-compiled profile, so the Compose runtime, the layout system and your composables all run interpreted until JIT catches up. Compose puts far more framework code on the critical path than the View system, which is why the effect is more visible here.
Classes. Every composable lambda is a class. A single screen loads hundreds, and loading plus verification happens on the thread producing the frame.
Composition. The first pass allocates every slot-table group and every LayoutNode; later passes only diff. Inside a LazyColumn, each distinct item layout pays this again the first time it scrolls into view.
Draw. RenderThread compiles the shader programs for effects it has not drawn before, and the text stack rasterizes each glyph into the atlas once per typeface and size.
What fixes which part
A baseline profile fixes point 1, and it covers androidx.compose.* as well as your own code because those classes ship inside your APK, which is why Compose apps gain more from one than View apps do. Generate it with the androidx.baselineprofile plugin and a BaselineProfileRule test that drives this journey: launch, open the screen, fling the list.
Points 2 to 4 gain little from AOT. Fewer distinct item layouts, honest contentType values and prefetching a couple of rows off-screen are what cut them.
Verify rather than assume: run the scroll with CompilationMode.None() and again with CompilationMode.Partial() and compare FrameTimingMetric.
The quick tell that you are looking at a first-run problem and not at your own code: adb shell cmd package compile -f -m speed <pkg> compiles everything ahead of time, and if the jank vanishes it was never your composables.
Look for offscreen buffers and fill rate: an alpha or a blur that forces the renderer to allocate a layer, shadows and non-rectangular clips, and stacked opaque backgrounds painting the same pixels several times over.
The usual offenders
Alpha. Modifier.alpha(0.5f) is graphicsLayer { alpha = 0.5f }, and CompositingStrategy.Auto promotes a layer with alpha below 1 into an offscreen buffer so overlapping children do not double-blend. When the children do not overlap, CompositingStrategy.ModulateAlpha applies the alpha per draw operation and skips the buffer entirely.
Shadows and clipping. Modifier.shadow(elevation, shape, clip = true) and Surface(shadowElevation = ...) draw from an outline. A rounded rectangle has a fast path; an arbitrary GenericShape becomes a path clip plus its own layer, on every row of the list.
Blur. Modifier.blur() is a RenderEffect (API 31 and up, a no-op below), and it re-renders its subtree into a buffer on every frame its content changes.
Overdraw. A Surface inside a Surface inside a Box(Modifier.background(...)) paints the same pixels three times. Across a full-screen list that is pure fill rate.
Blamed on the GPU but not GPU work: painterResource decodes on the calling thread during composition, and a bitmap decoded at full resolution for a thumbnail costs memory and fill both.
Measuring instead of guessing
Developer options, Debug GPU overdraw, colors each pixel by how many times it was painted. Blue is fine, red is a bug you can see without a profiler.
A Perfetto system trace puts RenderThread next to the frame timeline: long DrawFrame slices while the UI thread sits idle means the problem is draw, not recomposition.
adb shell dumpsys gfxinfo <pkg> framestats gives the per-frame breakdown, and FrameTimingMetric turns the same data into a number you can track over time.
The mental model: composition decides what to draw, but the renderer pays for how many layers and how many times over. Every offscreen buffer is a texture allocated, drawn into, then drawn again.
Use two gates with different characters: a compiler-metrics diff that is fully deterministic and runs in seconds on every PR, and a Macrobenchmark scroll on one fixed device that only means anything once you control the noise.
The cheap deterministic gate
Turn on composeCompiler { metricsDestination = ...; reportsDestination = ... } and commit the generated *-module.json per module. It records skippableComposables, restartableComposables and the counts of stable and unstable parameters: pure compiler output, byte-identical for identical sources.
CI rebuilds it and diffs against the committed copy. A rise in knownUnstableArguments, or a new unstable class in *-classes.txt, fails the PR with a one-line reason. No device, no flake, no minutes.
Its blind spot: it says nothing about runtime behavior. A perfectly skippable composable that gets invalidated on every frame still passes.
The scroll gate
Macrobenchmark with FrameTimingMetric, a scripted fling over fixed seeded data: no network, no live clock, no variable-length animation. Report frameDurationCpuMs and frameOverrunMs at P50/P90/P99; the tail is where jank lives, the median hides it.
Pin what dominates the number: one device model and OS image, locked clocks, CompilationMode.Partial() because that is what a real install runs, at least 10 iterations, the first discarded as warmup.
Compare the PR against its merge base in the same job on the same device, not against a figure recorded last week. Thermal state and an OS update move the absolute number far more than most regressions do.
Gate on a relative threshold with slack in it, around 10-15% on P90, and run it on the merge queue or behind a label, because a device job costs minutes.
The failure mode to design against is not a missed regression, it is a flaky gate: one red build nobody can reproduce and the whole thing gets switched off within a month.