Skip to content

Commit

Permalink
[JENKINS-21921] Lazy loaded report details are never released
Browse files Browse the repository at this point in the history
- SloccountResult.lazyLoad() caches the data to a member variable. SloccountResult objects (all build.xml files) are loaded during Jenkins startup and never released so Java garbage collector has no chance to trash the lazy loaded details since the reference exists forever. This is kind of a memory leak.
- Lazy loading reads the data everytime and doesn't use any cache, saving of memory is now prefered to performace. It is not expected the user will show the details very often, so it should be ok.
- Explicit calls of convertLegacyData() replaced by readResolve() from Java serialization with the same effect.
- Report object made transient, null is always passed in SloccountPublisher while storing.
  • Loading branch information
mixalturek committed Mar 12, 2014
1 parent f336a18 commit 32b1fa8
Showing 1 changed file with 22 additions and 23 deletions.
45 changes: 22 additions & 23 deletions src/main/java/hudson/plugins/sloccount/SloccountResult.java
Expand Up @@ -11,7 +11,7 @@
import hudson.plugins.sloccount.model.SloccountReportStatistics;

import java.io.Serializable;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.List;

/**
Expand All @@ -22,7 +22,8 @@ public class SloccountResult implements Serializable {
/** Serial version UID. */
private static final long serialVersionUID = 0L;

private SloccountReport report;
private transient SloccountReport report;

private final AbstractBuild<?,?> owner;

/** The statistics. */
Expand All @@ -40,8 +41,7 @@ public SloccountResult(SloccountReportStatistics statistics, String encoding,
}

public SloccountReport getReport() {
lazyLoad();
return report;
return lazyLoadReport();
}

public AbstractBuild<?,?> getOwner() {
Expand All @@ -54,56 +54,55 @@ public AbstractBuild<?,?> getOwner() {
* @return the statistics, always non-null value
*/
public SloccountReportStatistics getStatistics() {
convertLegacyData();
return statistics;
}

/**
* Convert legacy data in format of sloccount plugin version 1.10
* to the new one that uses statistics.
* Convert legacy data in format of plugin version 1.10 to the new one that
* uses statistics.
*
* If statistics are null for any reason, the object will be created.
* Statistics will be always non-null after this method is called (side
* effect).
* @return this with optionally updated data
*/
private void convertLegacyData() {
if(statistics != null) {
return;
}

List<SloccountLanguageStatistics> languages = new LinkedList<SloccountLanguageStatistics>();
private Object readResolve() {
if (report != null && statistics == null) {
List<SloccountLanguageStatistics> languages = new ArrayList<SloccountLanguageStatistics>();

if(report != null) {
for(Language language : report.getLanguages()){
languages.add(new SloccountLanguageStatistics(language.getName(),
language.getLineCount(), language.getFileCount()));
}

statistics = new SloccountReportStatistics(languages);
}

// Just for sure
if (statistics == null) {
statistics = new SloccountReportStatistics(new ArrayList<SloccountLanguageStatistics>());
}

statistics = new SloccountReportStatistics(languages);
return this;
}

/**
* Lazy load report data if they are not already loaded.
*/
private void lazyLoad() {
private SloccountReport lazyLoadReport() {
if(report != null) {
return;
return report;
}

java.io.File destDir = new java.io.File(owner.getRootDir(),
SloccountPublisher.BUILD_SUBDIR);

if (!destDir.exists()) {
report = new SloccountReport();
return;
return new SloccountReport();
}

String realEncoding = (encoding != null && !encoding.isEmpty())
? encoding : SloccountPublisher.DEFAULT_ENCODING;

SloccountParser parser = new SloccountParser(realEncoding, null, null);
report = parser.parseFiles(destDir.listFiles());
return parser.parseFiles(destDir.listFiles());
}

/**
Expand Down

0 comments on commit 32b1fa8

Please sign in to comment.