When I have a large presentation (also works with small, but with large ones it’s more obvious.
It happens when I try to split a presentation into it’s slides. See my testcode:
package de.testaspose
import com.aspose.slides.LoadOptions
import com.aspose.slides.Presentation
import com.aspose.slides.PresentationLockingBehavior
import com.aspose.slides.SaveFormat
fun main() {
val loadOptions = LoadOptions()
loadOptions.blobManagementOptions.presentationLockingBehavior = PresentationLockingBehavior.KeepLocked;
loadOptions.blobManagementOptions.isTemporaryFilesAllowed = true;
loadOptions.blobManagementOptions.tempFilesRootPath = "/Users/arturhellmann/DEV/tests/data/"
loadOptions.blobManagementOptions.maxBlobsBytesInMemory = 10 * 1024 * 1024; // 10 MB
val presName = "TestPres"
val sourcePresentation = Presentation(
"/Users/arturhellmann/DEV/tests/data/${presName}.pptx",
loadOptions
)
var mostUsedHeap = 0.0
sourcePresentation.slides.forEach { slideToKeep ->
val newPresentation = Presentation()
newPresentation.slides.removeAt(0)
newPresentation.masters.removeAt(0)
newPresentation.slides.addClone(slideToKeep) // Only this line leads to a memory peak.
newPresentation.save(
"/Users/arturhellmann/DEV/tests/data/result/TestPres_${slideToKeep.slideNumber}.pptx", SaveFormat.Pptx
)
val usedHeap = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()).toDouble() / 1024 / 1024
println("Used heap size: $usedHeap MB in slide ${slideToKeep.slideNumber}")
mostUsedHeap = if (usedHeap > mostUsedHeap) usedHeap else mostUsedHeap
newPresentation.dispose()
System.gc()
}
println("Most used heap size: $mostUsedHeap MB")
sourcePresentation.dispose()
System.gc()
readLine()
}
Using .addClone(...) leads to accumulating memory that will not be cleaned. Just closing the sourcePresentation by sourcePresentation.dispose() does release the memory. so it is not really a memoryleak, but it’s keeping a large bunch of memory as long as the presentation is open.
But that means that iterating over large presentations is really memory consuming.
My workaround for now is to always close the source presentation and reopen it, like so:
import com.aspose.slides.LoadOptions
import com.aspose.slides.Presentation
import com.aspose.slides.PresentationLockingBehavior
import com.aspose.slides.SaveFormat
fun main() {
val loadOptions = LoadOptions()
loadOptions.blobManagementOptions.presentationLockingBehavior = PresentationLockingBehavior.KeepLocked;
loadOptions.blobManagementOptions.isTemporaryFilesAllowed = true;
loadOptions.blobManagementOptions.tempFilesRootPath = "/Users/arturhellmann/DEV/dockertests/data/"
loadOptions.blobManagementOptions.maxBlobsBytesInMemory = 10 * 1024 * 1024; // 10 MB
val presName = "TestPres"
val sourcePresentation = Presentation(
"/Users/arturhellmann/DEV/dockertests/data/${presName}.pptx",
loadOptions
)
val numberOfSlides = sourcePresentation.slides.size()
sourcePresentation.dispose()
var mostUsedHeap = 0.0
for (index in 0 until numberOfSlides) {
val sourcePresentation = Presentation(
"/Users/arturhellmann/DEV/dockertests/data/${presName}.pptx",
loadOptions
)
val slideToKeep = sourcePresentation.slides.get_Item(index)
val newPresentation = Presentation()
newPresentation.slides.removeAt(0)
newPresentation.masters.removeAt(0)
newPresentation.slides.addClone(slideToKeep) // Only this line leads to a memory peak.
newPresentation.save(
"/Users/arturhellmann/DEV/dockertests/data/result/TestPres_${slideToKeep.slideNumber}.pptx", SaveFormat.Pptx
)
val usedHeap = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()).toDouble() / 1024 / 1024
println("Used heap size: $usedHeap MB in slide ${slideToKeep.slideNumber}")
mostUsedHeap = if (usedHeap > mostUsedHeap) usedHeap else mostUsedHeap
newPresentation.dispose()
sourcePresentation.dispose()
System.gc()
}
println("Most used heap size: $mostUsedHeap MB")
sourcePresentation.dispose()
System.gc()
readLine()
}
But for large files, this is obviously seriously slow…
Is there some better way to iterate over slides and add them to new presentations, or maybe a native split presentation method like in the cloud api?
Regards
Artur