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.
This commit is contained in:
Ben Gruver 2014-01-07 13:53:03 -08:00
parent 532c04b27d
commit 90db3a16b7

View File

@ -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 org.jf.dexlib2.iface.reference.TypeReference;
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 @@ public class MethodLocation {
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 @@ public class MethodLocation {
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 @@ public class MethodLocation {
}
@Override public int size() {
return labels.size();
return getLabels(false).size();
}
@Override public boolean add(@Nonnull Label label) {
@ -124,7 +159,7 @@ public class MethodLocation {
"it from its current location first.");
}
label.location = MethodLocation.this;
labels.add(label);
getLabels(true).add(label);
return true;
}
};
@ -133,7 +168,7 @@ public class MethodLocation {
@Nonnull
public Label addNewLabel() {
Label label = new Label(this);
labels.add(label);
getLabels(true).add(label);
return label;
}
@ -142,7 +177,7 @@ public class MethodLocation {
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 @@ public class MethodLocation {
}
@Override public int size() {
return labels.size();
return getDebugItems(false).size();
}
@Override public boolean add(@Nonnull BuilderDebugItem debugItem) {
@ -175,7 +210,7 @@ public class MethodLocation {
"method. You must remove it from its current location first.");
}
debugItem.location = MethodLocation.this;
debugItems.add(debugItem);
getDebugItems(true).add(debugItem);
return true;
}
};