Don't leak ObjectReaders created from ObjectInserters

We had been pretty lazy about calling new RevWalk(inserter.newReader()).
I can't say with certainty for any individual case, but there are
at least two possible loose justifications for this:

1. ObjectInserter#newReader() feels like it shouldn't allocate any new
   resources (other than GCable buffers).
2. It seems like the RevWalk might close the reader.

Both of these are bad arguments. For (1), WindowCursor acquires an
inflater from the inflater cache, so failing to release the inflater
results in more allocations. For (2), RevWalk does not close the reader
when using this constructor. While perhaps non-obvious, this behavior is
clearly documented and stable.

Change-Id: I712973ecfcca439245722c777abde129dbe3773d
This commit is contained in:
Dave Borowitz
2017-04-03 11:51:00 -04:00
parent 178b7174cc
commit d3239cced2
8 changed files with 38 additions and 19 deletions

View File

@@ -74,6 +74,7 @@ import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.RefDatabase;
@@ -275,7 +276,8 @@ public class RebuildNoteDb extends SiteProgram {
pm.beginTask(FormatUtil.elide(project.get(), 50), allChanges.get(project).size()); pm.beginTask(FormatUtil.elide(project.get(), 50), allChanges.get(project).size());
try (NoteDbUpdateManager manager = updateManagerFactory.create(project); try (NoteDbUpdateManager manager = updateManagerFactory.create(project);
ObjectInserter allUsersInserter = allUsersRepo.newObjectInserter(); ObjectInserter allUsersInserter = allUsersRepo.newObjectInserter();
RevWalk allUsersRw = new RevWalk(allUsersInserter.newReader())) { ObjectReader reader = allUsersInserter.newReader();
RevWalk allUsersRw = new RevWalk(reader)) {
manager.setAllUsersRepo( manager.setAllUsersRepo(
allUsersRepo, allUsersRw, allUsersInserter, new ChainedReceiveCommands(allUsersRepo)); allUsersRepo, allUsersRw, allUsersInserter, new ChainedReceiveCommands(allUsersRepo));
for (Change.Id changeId : allChanges.get(project)) { for (Change.Id changeId : allChanges.get(project)) {

View File

@@ -64,6 +64,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
@@ -137,7 +138,8 @@ public class CherryPickChange {
// created later on, to ensure the cherry-picked commit is flushed // created later on, to ensure the cherry-picked commit is flushed
// before patch sets are updated. // before patch sets are updated.
ObjectInserter oi = git.newObjectInserter(); ObjectInserter oi = git.newObjectInserter();
CodeReviewRevWalk revWalk = CodeReviewCommit.newRevWalk(oi.newReader())) { ObjectReader reader = oi.newReader();
CodeReviewRevWalk revWalk = CodeReviewCommit.newRevWalk(reader)) {
Ref destRef = git.getRefDatabase().exactRef(ref); Ref destRef = git.getRefDatabase().exactRef(ref);
if (destRef == null) { if (destRef == null) {
throw new InvalidChangeOperationException( throw new InvalidChangeOperationException(

View File

@@ -76,6 +76,7 @@ import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
@@ -184,7 +185,8 @@ public class CreateChange implements RestModifyView<TopLevelResource, ChangeInpu
Project.NameKey project = rsrc.getNameKey(); Project.NameKey project = rsrc.getNameKey();
try (Repository git = gitManager.openRepository(project); try (Repository git = gitManager.openRepository(project);
ObjectInserter oi = git.newObjectInserter(); ObjectInserter oi = git.newObjectInserter();
RevWalk rw = new RevWalk(oi.newReader())) { ObjectReader reader = oi.newReader();
RevWalk rw = new RevWalk(reader)) {
ObjectId parentCommit; ObjectId parentCommit;
List<String> groups; List<String> groups;
if (input.baseChange != null) { if (input.baseChange != null) {

View File

@@ -56,6 +56,7 @@ import java.sql.Timestamp;
import java.util.TimeZone; import java.util.TimeZone;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
@@ -126,7 +127,8 @@ public class CreateMergePatchSet implements RestModifyView<ChangeResource, Merge
Branch.NameKey dest = change.getDest(); Branch.NameKey dest = change.getDest();
try (Repository git = gitManager.openRepository(project); try (Repository git = gitManager.openRepository(project);
ObjectInserter oi = git.newObjectInserter(); ObjectInserter oi = git.newObjectInserter();
RevWalk rw = new RevWalk(oi.newReader())) { ObjectReader reader = oi.newReader();
RevWalk rw = new RevWalk(reader)) {
RevCommit sourceCommit = MergeUtil.resolveCommit(git, rw, merge.source); RevCommit sourceCommit = MergeUtil.resolveCommit(git, rw, merge.source);
if (!projectControl.canReadCommit(db.get(), git, sourceCommit)) { if (!projectControl.canReadCommit(db.get(), git, sourceCommit)) {

View File

@@ -41,6 +41,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
@@ -89,7 +90,8 @@ public class GetBlame implements RestReadView<FileResource> {
Project.NameKey project = resource.getRevision().getChange().getProject(); Project.NameKey project = resource.getRevision().getChange().getProject();
try (Repository repository = repoManager.openRepository(project); try (Repository repository = repoManager.openRepository(project);
ObjectInserter ins = repository.newObjectInserter(); ObjectInserter ins = repository.newObjectInserter();
RevWalk revWalk = new RevWalk(ins.newReader())) { ObjectReader reader = ins.newReader();
RevWalk revWalk = new RevWalk(reader)) {
String refName = String refName =
resource.getRevision().getEdit().isPresent() resource.getRevision().getEdit().isPresent()
? resource.getRevision().getEdit().get().getRefName() ? resource.getRevision().getEdit().get().getRefName()

View File

@@ -178,6 +178,7 @@ public class NoteDbUpdateManager implements AutoCloseable {
@Override @Override
public void close() { public void close() {
rw.getObjectReader().close();
rw.close(); rw.close();
if (close) { if (close) {
if (finalIns != null) { if (finalIns != null) {
@@ -295,10 +296,18 @@ public class NoteDbUpdateManager implements AutoCloseable {
} }
private OpenRepo openRepo(Project.NameKey p) throws IOException { private OpenRepo openRepo(Project.NameKey p) throws IOException {
Repository repo = repoManager.openRepository(p); Repository repo = repoManager.openRepository(p); // Closed by OpenRepo#close.
ObjectInserter ins = repo.newObjectInserter(); ObjectInserter ins = repo.newObjectInserter(); // Closed by OpenRepo#close.
return new OpenRepo( ObjectReader reader = ins.newReader(); // Not closed by OpenRepo#close.
repo, new RevWalk(ins.newReader()), ins, new ChainedReceiveCommands(repo), true); try (RevWalk rw = new RevWalk(reader)) { // Doesn't escape OpenRepo constructor.
return new OpenRepo(repo, rw, ins, new ChainedReceiveCommands(repo), true) {
@Override
public void close() {
reader.close();
super.close();
}
};
}
} }
private boolean isEmpty() { private boolean isEmpty() {

View File

@@ -189,6 +189,7 @@ public abstract class BatchUpdate implements AutoCloseable {
@Override @Override
public void close() { public void close() {
if (closeRepo) { if (closeRepo) {
revWalk.getObjectReader().close();
revWalk.close(); revWalk.close();
inserter.close(); inserter.close();
repo.close(); repo.close();

View File

@@ -85,6 +85,7 @@ import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
@@ -440,16 +441,14 @@ class ReviewDbBatchUpdate extends BatchUpdate {
} }
if (onSubmitValidators != null && commands != null && !commands.isEmpty()) { if (onSubmitValidators != null && commands != null && !commands.isEmpty()) {
// Validation of refs has to take place here and not at the beginning try (ObjectReader reader = ctx.getInserter().newReader()) {
// executeRefUpdates. Otherwise failing validation in a second // Validation of refs has to take place here and not at the beginning
// BatchUpdate object will happen *after* first object's // executeRefUpdates. Otherwise failing validation in a second BatchUpdate object will
// executeRefUpdates has finished, hence after first repo's refs have // happen *after* first object's executeRefUpdates has finished, hence after first repo's
// been updated, which is too late. // refs have been updated, which is too late.
onSubmitValidators.validate( onSubmitValidators.validate(
project, project, new ReadOnlyRepository(getRepository()), reader, commands.getCommands());
new ReadOnlyRepository(getRepository()), }
ctx.getInserter().newReader(),
commands.getCommands());
} }
if (inserter != null) { if (inserter != null) {