HDFS-12042. Lazy initialize AbstractINodeDiffList#diffs for snapshots to reduce memory consumption. Contributed by Misha Dmitriev.

(cherry picked from commit bcba844d11)
This commit is contained in:
Wei-Chiu Chuang 2017-06-30 10:28:01 -07:00
parent 2d69952925
commit 94bc5cdbb3
3 changed files with 46 additions and 15 deletions

View File

@ -65,8 +65,11 @@ public class INodeDirectory extends INodeWithAdditionalFields
return inode.asDirectory(); return inode.asDirectory();
} }
protected static final int DEFAULT_FILES_PER_DIRECTORY = 5; // Profiling shows that most of the file lists are between 1 and 4 elements.
final static byte[] ROOT_NAME = DFSUtil.string2Bytes(""); // Thus allocate the corresponding ArrayLists with a small initial capacity.
public static final int DEFAULT_FILES_PER_DIRECTORY = 2;
static final byte[] ROOT_NAME = DFSUtil.string2Bytes("");
private List<INode> children = null; private List<INode> children = null;

View File

@ -24,6 +24,7 @@ import java.util.List;
import org.apache.hadoop.hdfs.server.namenode.INode; import org.apache.hadoop.hdfs.server.namenode.INode;
import org.apache.hadoop.hdfs.server.namenode.INodeAttributes; import org.apache.hadoop.hdfs.server.namenode.INodeAttributes;
import org.apache.hadoop.hdfs.server.namenode.INodeDirectory;
/** /**
* A list of snapshot diffs for storing snapshot data. * A list of snapshot diffs for storing snapshot data.
@ -35,17 +36,19 @@ abstract class AbstractINodeDiffList<N extends INode,
A extends INodeAttributes, A extends INodeAttributes,
D extends AbstractINodeDiff<N, A, D>> D extends AbstractINodeDiff<N, A, D>>
implements Iterable<D> { implements Iterable<D> {
/** Diff list sorted by snapshot IDs, i.e. in chronological order. */ /** Diff list sorted by snapshot IDs, i.e. in chronological order.
private final List<D> diffs = new ArrayList<D>(); * Created lazily to avoid wasting memory by empty lists. */
private List<D> diffs;
/** @return this list as a unmodifiable {@link List}. */ /** @return this list as a unmodifiable {@link List}. */
public final List<D> asList() { public final List<D> asList() {
return Collections.unmodifiableList(diffs); return diffs != null ?
Collections.unmodifiableList(diffs) : Collections.<D>emptyList();
} }
/** Get the size of the list and then clear it. */ /** Clear the list. */
public void clear() { public void clear() {
diffs.clear(); diffs = null;
} }
/** @return an {@link AbstractINodeDiff}. */ /** @return an {@link AbstractINodeDiff}. */
@ -66,6 +69,9 @@ abstract class AbstractINodeDiffList<N extends INode,
*/ */
public final void deleteSnapshotDiff(INode.ReclaimContext reclaimContext, public final void deleteSnapshotDiff(INode.ReclaimContext reclaimContext,
final int snapshot, final int prior, final N currentINode) { final int snapshot, final int prior, final N currentINode) {
if (diffs == null) {
return;
}
int snapshotIndex = Collections.binarySearch(diffs, snapshot); int snapshotIndex = Collections.binarySearch(diffs, snapshot);
D removed; D removed;
@ -75,6 +81,9 @@ abstract class AbstractINodeDiffList<N extends INode,
diffs.get(snapshotIndex).setSnapshotId(prior); diffs.get(snapshotIndex).setSnapshotId(prior);
} else { // there is no snapshot before } else { // there is no snapshot before
removed = diffs.remove(0); removed = diffs.remove(0);
if (diffs.isEmpty()) {
diffs = null;
}
removed.destroyDiffAndCollectBlocks(reclaimContext, currentINode); removed.destroyDiffAndCollectBlocks(reclaimContext, currentINode);
} }
} else if (snapshotIndex > 0) { } else if (snapshotIndex > 0) {
@ -103,6 +112,7 @@ abstract class AbstractINodeDiffList<N extends INode,
/** Append the diff at the end of the list. */ /** Append the diff at the end of the list. */
private D addLast(D diff) { private D addLast(D diff) {
createDiffsIfNeeded();
final D last = getLast(); final D last = getLast();
diffs.add(diff); diffs.add(diff);
if (last != null) { if (last != null) {
@ -113,15 +123,25 @@ abstract class AbstractINodeDiffList<N extends INode,
/** Add the diff to the beginning of the list. */ /** Add the diff to the beginning of the list. */
final void addFirst(D diff) { final void addFirst(D diff) {
final D first = diffs.isEmpty()? null: diffs.get(0); createDiffsIfNeeded();
final D first = diffs.isEmpty()? null : diffs.get(0);
diffs.add(0, diff); diffs.add(0, diff);
diff.setPosterior(first); diff.setPosterior(first);
} }
/** @return the last diff. */ /** @return the last diff. */
public final D getLast() { public final D getLast() {
final int n = diffs.size(); if (diffs == null) {
return n == 0? null: diffs.get(n - 1); return null;
}
int n = diffs.size();
return n == 0 ? null : diffs.get(n - 1);
}
private void createDiffsIfNeeded() {
if (diffs == null) {
diffs = new ArrayList<>(INodeDirectory.DEFAULT_FILES_PER_DIRECTORY);
}
} }
/** @return the id of the last snapshot. */ /** @return the id of the last snapshot. */
@ -139,10 +159,14 @@ abstract class AbstractINodeDiffList<N extends INode,
* @return The id of the latest snapshot before the given snapshot. * @return The id of the latest snapshot before the given snapshot.
*/ */
public final int getPrior(int anchorId, boolean exclusive) { public final int getPrior(int anchorId, boolean exclusive) {
if (diffs == null) {
return Snapshot.NO_SNAPSHOT_ID;
}
if (anchorId == Snapshot.CURRENT_STATE_ID) { if (anchorId == Snapshot.CURRENT_STATE_ID) {
int last = getLastSnapshotId(); int last = getLastSnapshotId();
if(exclusive && last == anchorId) if (exclusive && last == anchorId) {
return Snapshot.NO_SNAPSHOT_ID; return Snapshot.NO_SNAPSHOT_ID;
}
return last; return last;
} }
final int i = Collections.binarySearch(diffs, anchorId); final int i = Collections.binarySearch(diffs, anchorId);
@ -181,7 +205,7 @@ abstract class AbstractINodeDiffList<N extends INode,
} }
public final D getDiffById(final int snapshotId) { public final D getDiffById(final int snapshotId) {
if (snapshotId == Snapshot.CURRENT_STATE_ID) { if (snapshotId == Snapshot.CURRENT_STATE_ID || diffs == null) {
return null; return null;
} }
final int i = Collections.binarySearch(diffs, snapshotId); final int i = Collections.binarySearch(diffs, snapshotId);
@ -193,7 +217,7 @@ abstract class AbstractINodeDiffList<N extends INode,
// given snapshot and the next state so that the diff for the given // given snapshot and the next state so that the diff for the given
// snapshot was not recorded. Thus, return the next state. // snapshot was not recorded. Thus, return the next state.
final int j = -i - 1; final int j = -i - 1;
return j < diffs.size()? diffs.get(j): null; return j < diffs.size() ? diffs.get(j) : null;
} }
} }
@ -207,6 +231,9 @@ abstract class AbstractINodeDiffList<N extends INode,
} }
final int[] changedBetweenSnapshots(Snapshot from, Snapshot to) { final int[] changedBetweenSnapshots(Snapshot from, Snapshot to) {
if (diffs == null) {
return null;
}
Snapshot earlier = from; Snapshot earlier = from;
Snapshot later = to; Snapshot later = to;
if (Snapshot.ID_COMPARATOR.compare(from, to) > 0) { if (Snapshot.ID_COMPARATOR.compare(from, to) > 0) {
@ -275,11 +302,11 @@ abstract class AbstractINodeDiffList<N extends INode,
@Override @Override
public Iterator<D> iterator() { public Iterator<D> iterator() {
return diffs.iterator(); return diffs != null ? diffs.iterator() : Collections.<D>emptyIterator();
} }
@Override @Override
public String toString() { public String toString() {
return getClass().getSimpleName() + ": " + diffs; return getClass().getSimpleName() + ": " + (diffs != null ? diffs : "[]");
} }
} }

View File

@ -156,6 +156,7 @@ public class TestTruncateQuotaUpdate {
FileDiff diff = mock(FileDiff.class); FileDiff diff = mock(FileDiff.class);
when(diff.getBlocks()).thenReturn(blocks); when(diff.getBlocks()).thenReturn(blocks);
FileDiffList diffList = new FileDiffList(); FileDiffList diffList = new FileDiffList();
Whitebox.setInternalState(diffList, "diffs", new ArrayList<FileDiff>());
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
ArrayList<FileDiff> diffs = ((ArrayList<FileDiff>)Whitebox.getInternalState ArrayList<FileDiff> diffs = ((ArrayList<FileDiff>)Whitebox.getInternalState
(diffList, "diffs")); (diffList, "diffs"));