From 32b1fa89cbe28eeb0831db3d1f9dae39bb102a35 Mon Sep 17 00:00:00 2001 From: Michal Turek Date: Wed, 12 Mar 2014 22:23:47 +0100 Subject: [PATCH] [JENKINS-21921] Lazy loaded report details are never released - 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. --- .../plugins/sloccount/SloccountResult.java | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/main/java/hudson/plugins/sloccount/SloccountResult.java b/src/main/java/hudson/plugins/sloccount/SloccountResult.java index c1eaf53..8926d1a 100644 --- a/src/main/java/hudson/plugins/sloccount/SloccountResult.java +++ b/src/main/java/hudson/plugins/sloccount/SloccountResult.java @@ -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; /** @@ -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. */ @@ -40,8 +41,7 @@ public SloccountResult(SloccountReportStatistics statistics, String encoding, } public SloccountReport getReport() { - lazyLoad(); - return report; + return lazyLoadReport(); } public AbstractBuild getOwner() { @@ -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 languages = new LinkedList(); + private Object readResolve() { + if (report != null && statistics == null) { + List languages = new ArrayList(); - 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()); } - 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()); } /**