Lazily create the lists in MethodLocation
Millions of MethodLocations can be created and kept in memory when
using the Builder interface to build a large dex file. The arrays
backing these lists were taking up a large amount of memory.
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java b/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java
index f7c0eff..5a707e9 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/builder/MethodLocation.java
@@ -31,7 +31,7 @@
package org.jf.dexlib2.builder;
-import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
import org.jf.dexlib2.builder.debug.*;
import org.jf.dexlib2.iface.instruction.Instruction;
import org.jf.dexlib2.iface.reference.StringReference;
@@ -39,18 +39,21 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import java.util.AbstractSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
public class MethodLocation {
@Nullable BuilderInstruction instruction;
int codeAddress;
int index;
- private List<Label> labels = Lists.newArrayList();
- List<BuilderDebugItem> debugItems = Lists.newArrayList();
+ // We end up creating and keeping around a *lot* of MethodLocation objects
+ // when building a new dex file, so it's worth the trouble of lazily creating
+ // the labels and debugItems lists only when they are needed
+
+ @Nullable
+ private List<Label> labels = null;
+ @Nullable
+ private List<BuilderDebugItem> debugItems = null;
MethodLocation(@Nullable BuilderInstruction instruction, int codeAddress, int index) {
this.instruction = instruction;
@@ -71,19 +74,51 @@
return index;
}
+ @Nonnull
+ private List<Label> getLabels(boolean mutable) {
+ if (labels == null) {
+ if (mutable) {
+ labels = new ArrayList<Label>(1);
+ return labels;
+ }
+ return ImmutableList.of();
+ }
+ return labels;
+ }
+
+ @Nonnull
+ private List<BuilderDebugItem> getDebugItems(boolean mutable) {
+ if (debugItems == null) {
+ if (mutable) {
+ debugItems = new ArrayList<BuilderDebugItem>(1);
+ return debugItems;
+ }
+ return ImmutableList.of();
+ }
+ return debugItems;
+ }
+
void mergeInto(@Nonnull MethodLocation other) {
- for (Label label: labels) {
- label.location = other;
- other.labels.add(label);
+ if (this.labels != null || other.labels != null) {
+ List<Label> otherLabels = other.getLabels(true);
+ for (Label label: this.getLabels(false)) {
+ label.location = other;
+ otherLabels.add(label);
+ }
+ this.labels = null;
}
- // We need to keep the debug items in the same order. We add the other debug items to this list, then reassign
- // the list.
- for (BuilderDebugItem debugItem: debugItems) {
- debugItem.location = other;
+ if (this.debugItems != null || other.labels != null) {
+ // We need to keep the debug items in the same order. We add the other debug items to this list, then reassign
+ // the list.
+ List<BuilderDebugItem> debugItems = getDebugItems(true);
+ for (BuilderDebugItem debugItem: debugItems) {
+ debugItem.location = other;
+ }
+ debugItems.addAll(other.getDebugItems(false));
+ other.debugItems = debugItems;
+ this.debugItems = null;
}
- debugItems.addAll(other.debugItems);
- other.debugItems = debugItems;
}
@Nonnull
@@ -91,7 +126,7 @@
return new AbstractSet<Label>() {
@Nonnull
@Override public Iterator<Label> iterator() {
- final Iterator<Label> it = labels.iterator();
+ final Iterator<Label> it = getLabels(false).iterator();
return new Iterator<Label>() {
private @Nullable Label currentLabel = null;
@@ -115,7 +150,7 @@
}
@Override public int size() {
- return labels.size();
+ return getLabels(false).size();
}
@Override public boolean add(@Nonnull Label label) {
@@ -124,7 +159,7 @@
"it from its current location first.");
}
label.location = MethodLocation.this;
- labels.add(label);
+ getLabels(true).add(label);
return true;
}
};
@@ -133,7 +168,7 @@
@Nonnull
public Label addNewLabel() {
Label label = new Label(this);
- labels.add(label);
+ getLabels(true).add(label);
return label;
}
@@ -142,7 +177,7 @@
return new AbstractSet<BuilderDebugItem>() {
@Nonnull
@Override public Iterator<BuilderDebugItem> iterator() {
- final Iterator<BuilderDebugItem> it = debugItems.iterator();
+ final Iterator<BuilderDebugItem> it = getDebugItems(false).iterator();
return new Iterator<BuilderDebugItem>() {
private @Nullable BuilderDebugItem currentDebugItem = null;
@@ -166,7 +201,7 @@
}
@Override public int size() {
- return labels.size();
+ return getDebugItems(false).size();
}
@Override public boolean add(@Nonnull BuilderDebugItem debugItem) {
@@ -175,7 +210,7 @@
"method. You must remove it from its current location first.");
}
debugItem.location = MethodLocation.this;
- debugItems.add(debugItem);
+ getDebugItems(true).add(debugItem);
return true;
}
};