Java Heap Space - OutOfMemoryError

I am working on an application that requires the reading of images from client files and displaying images in the interface. Later these images can be inserted into a MySQL database. I have working scripts for all required functions - reading bytes from file, visualizing in either a label or image component (I tried both methods to see if there would be any differnce in the memory issue), and then inserting into a database. In this case, I would like to focus solely on the image component and its code.

The problem that I am experiencing is when I load the bytes into the image or label component the memory gets incrementally higher until I reach the 1024MB limit. Smaller images such as 720 X 1280 will cause used memory increase up to 4MB increases while 3000X4000 images will cause used memory increases up to 40MB. These values never decrease; so ultimately, I will get this error. The following graph demonstrates how the memory is consumed every time I select a new file.

I use the file explorer component to select a file, then I read the data and write to a temp file which is than written to the path property of the image component. Here is the interface:

The error that is thrown is:

Traceback (most recent call last):
  File "<event:propertyChange>", line 18, in <module>
	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2216)
	at com.google.common.cache.LocalCache.get(LocalCache.java:4147)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4151)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:5140)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:5146)
	at com.inductiveautomation.ignition.client.images.ImageLoader.getImageData(ImageLoader.java:239)
	at com.inductiveautomation.ignition.client.images.ImageLoader.loadImageMemCache(ImageLoader.java:213)
	at com.inductiveautomation.ignition.client.images.ImageLoader.loadImage(ImageLoader.java:160)
	at jdk.internal.reflect.GeneratedMethodAccessor117.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
com.google.common.util.concurrent.ExecutionError: com.google.common.util.concurrent.ExecutionError: java.lang.OutOfMemoryError: Java heap space

	at org.python.core.Py.JavaError(Py.java:552)
	at org.python.core.Py.JavaError(Py.java:543)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:190)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:206)
	at org.python.core.PyObject.__call__(PyObject.java:497)
	at org.python.core.PyObject.__call__(PyObject.java:501)
	at org.python.core.PyMethod.__call__(PyMethod.java:141)
	at org.python.pycode._pyx42.f$0(<event:propertyChange>:42)
	at org.python.pycode._pyx42.call_function(<event:propertyChange>)
	at org.python.core.PyTableCode.call(PyTableCode.java:171)
	at org.python.core.PyCode.call(PyCode.java:18)
	at org.python.core.Py.runCode(Py.java:1614)
	at com.inductiveautomation.ignition.common.script.ScriptManager.runCode(ScriptManager.java:770)
	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.runActions(ActionAdapter.java:200)
	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter$ActionRunner.run(ActionAdapter.java:306)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Caused by: com.google.common.util.concurrent.ExecutionError: java.lang.OutOfMemoryError: Java heap space
	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2216)
	at com.google.common.cache.LocalCache.get(LocalCache.java:4147)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4151)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:5140)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:5146)
	at com.inductiveautomation.ignition.client.images.ImageLoader.getImageData(ImageLoader.java:239)
	at com.inductiveautomation.ignition.client.images.ImageLoader.loadImageMemCache(ImageLoader.java:213)
	at com.inductiveautomation.ignition.client.images.ImageLoader.loadImage(ImageLoader.java:160)
	at jdk.internal.reflect.GeneratedMethodAccessor117.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188)
	... 25 more
Caused by: java.lang.OutOfMemoryError: Java heap space
	at java.desktop/java.awt.image.DataBufferByte.<init>(Unknown Source)
	at java.desktop/java.awt.image.ComponentSampleModel.createDataBuffer(Unknown Source)
	at java.desktop/java.awt.image.Raster.createWritableRaster(Unknown Source)
	at java.desktop/javax.imageio.ImageTypeSpecifier.createBufferedImage(Unknown Source)
	at java.desktop/javax.imageio.ImageReader.getDestination(Unknown Source)
	at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(Unknown Source)
	at java.desktop/com.sun.imageio.plugins.jpeg.JPEGImageReader.read(Unknown Source)
	at com.inductiveautomation.ignition.client.images.ImageLoader$ImageInfo.loadImage(ImageLoader.java:427)
	at com.inductiveautomation.ignition.client.images.ImageLoader$ImageInfo.<init>(ImageLoader.java:377)
	at com.inductiveautomation.ignition.client.images.ImageLoader$1.load(ImageLoader.java:72)
	at com.inductiveautomation.ignition.client.images.ImageLoader$1.load(ImageLoader.java:68)
	at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3708)
	at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2416)
	at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2299)
	at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2212)
	at com.google.common.cache.LocalCache.get(LocalCache.java:4147)
	at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4151)
	at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:5140)
	at com.google.common.cache.LocalCache$LocalLoadingCache.getUnchecked(LocalCache.java:5146)
	at com.inductiveautomation.ignition.client.images.ImageLoader.getImageData(ImageLoader.java:239)
	at com.inductiveautomation.ignition.client.images.ImageLoader.loadImageMemCache(ImageLoader.java:213)
	at com.inductiveautomation.ignition.client.images.ImageLoader.loadImage(ImageLoader.java:170)
	at com.inductiveautomation.ignition.client.images.PathIcon$LoadImageTask.doInBackground(PathIcon.java:317)
	at com.inductiveautomation.ignition.client.images.PathIcon$LoadImageTask.doInBackground(PathIcon.java:298)
	at java.desktop/javax.swing.SwingWorker$1.call(Unknown Source)
	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
	at java.desktop/javax.swing.SwingWorker.run(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.base/java.lang.Thread.run(Unknown Source)

Ignition v8.0.9 (b2020021812)
Java: Azul Systems, Inc. 11.0.5

I have included all of the event code below, but this is the line that will ultimately cause the error:
img = ImageLoader.getInstance().loadImage(filename). Please note that I have isolated the memory usage increase to this part of the code.

if event.propertyName == 'path':
	
	from com.inductiveautomation.ignition.client.images import ImageLoader	

	imgComponent = event.source
	imgComponent.rotation = 0
	imgComponent.imgRotationValue = 0

	# Get image component size from Custom Properties
	initImgCompWidth = imgComponent.initImgCompWidth
	initImgCompHeight = imgComponent.initImgCompHeight
	
	#resize image component to initial values in custom properties
	system.gui.transform(component=imgComponent, newWidth=initImgCompWidth, newHeight=initImgCompHeight)

	#Find Aspect Ratio of Original Image
	filename = imgComponent.getPath()
	img = ImageLoader.getInstance().loadImage(filename) #this line causes the JVM heap error in the client or designer 
	imgW = img.getWidth()
	imgH = img.getHeight()
	imgRatio = float(imgW) / float(imgH)
	
	if imgRatio > 1: # means that picture has more pixels in the x (width) than y (height)
		modImgCompWidth = initImgCompWidth
		modImgCompHeight = int(initImgCompHeight / imgRatio)

	else:
		modImgCompWidth = int(initImgCompWidth * imgRatio)
		modImgCompHeight = initImgCompHeight
	
	event.source.modImgCompWidth = modImgCompWidth
	event.source.modImgCompHeight = modImgCompHeight
	
	#resize the image component to maintain image perspective
	system.gui.transform(component=imgComponent, newWidth=modImgCompWidth, newHeight=modImgCompHeight)
	
	# Set Strech Mode to "Parameters"
	imgComponent.setStretchMode(2)
		
	#Set Component Image Bounds
	imgComponent.setStretchHeight(modImgCompHeight)  
	imgComponent.setStretchWidth(modImgCompWidth)

Additional information showing the designer “About” page, designer performance, gateway performace (not an issue), and project properties:

Finally, my development machine has 64bit architecture with more than 32GB of RAM.

What, if any solutions are available for this issue?

Thank you in advance.

Does it still happen if you modify your code to call ImageLoader.getInstance().loadImageNoCache() instead?

Hello Kevin,

Just tried loadImageNoCache() and memory usage still increasing.

For the second argument, I used a null to bring the file in at its natural size, bu I got an error. Maybe configuring the dimension argument could help.

Hmm, using None for the null parameter should work.

Does it increase and crash still, or just increase and eventually GC?

Increases to a crash. Here is the graph:

Here is the code:

if event.propertyName == 'path':
	
	from com.inductiveautomation.ignition.client.images import ImageLoader	

	imgComponent = event.source
	imgComponent.rotation = 0
	imgComponent.imgRotationValue = 0

	# Get image component size from Custom Properties
	initImgCompWidth = imgComponent.initImgCompWidth
	initImgCompHeight = imgComponent.initImgCompHeight
	
	#resize image component to initial values in custom properties
	system.gui.transform(component=imgComponent, newWidth=initImgCompWidth, newHeight=initImgCompHeight)

	#Find Aspect Ratio of Original Image
	filename = imgComponent.getPath()
	img = ImageLoader.getInstance().loadImageNoCache(filename, None) #this line causes the JVM heap error in the client or designer 
	imgW = img.getWidth()
	imgH = img.getHeight()
	imgRatio = float(imgW) / float(imgH)
	
	if imgRatio > 1: # means that picture has more pixels in the x (width) than y (height)
		modImgCompWidth = initImgCompWidth
		modImgCompHeight = int(initImgCompHeight / imgRatio)

	else:
		modImgCompWidth = int(initImgCompWidth * imgRatio)
		modImgCompHeight = initImgCompHeight
	
	event.source.modImgCompWidth = modImgCompWidth
	event.source.modImgCompHeight = modImgCompHeight
	
	#resize the image component to maintain image perspective
	system.gui.transform(component=imgComponent, newWidth=modImgCompWidth, newHeight=modImgCompHeight)
	
	# Set Strech Mode to "Parameters"
	imgComponent.setStretchMode(2)
		
	#Set Component Image Bounds
	imgComponent.setStretchHeight(modImgCompHeight)  
	imgComponent.setStretchWidth(modImgCompWidth)


Here is the error:

Traceback (most recent call last):
  File "<event:propertyChange>", line 19, in <module>
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: java.lang.OutOfMemoryError: Java heap space

	at org.python.core.Py.JavaError(Py.java:552)
	at org.python.core.Py.JavaError(Py.java:543)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:190)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:206)
	at org.python.core.PyObject.__call__(PyObject.java:515)
	at org.python.core.PyObject.__call__(PyObject.java:519)
	at org.python.core.PyMethod.__call__(PyMethod.java:156)
	at org.python.pycode._pyx7.f$0(<event:propertyChange>:43)
	at org.python.pycode._pyx7.call_function(<event:propertyChange>)
	at org.python.core.PyTableCode.call(PyTableCode.java:171)
	at org.python.core.PyCode.call(PyCode.java:18)
	at org.python.core.Py.runCode(Py.java:1614)
	at com.inductiveautomation.ignition.common.script.ScriptManager.runCode(ScriptManager.java:770)
	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.runActions(ActionAdapter.java:200)
	at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter$ActionRunner.run(ActionAdapter.java:306)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
	at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.OutOfMemoryError: Java heap space

Ignition v8.0.9 (b2020021812)
Java: Azul Systems, Inc. 11.0.5

Garbage collection never kicked in.

I forgot to provide my ignition.conf file:

#encoding=UTF-8
# Configuration files must begin with a line specifying the encoding
#  of the the file.

#********************************************************************
# Wrapper License Properties (Ignored by Community Edition)
#********************************************************************
# Professional and Standard Editions of the Wrapper require a valid
#  License Key to start.  Licenses can be purchased or a trial license
#  requested on the following pages:
# http://wrapper.tanukisoftware.com/purchase
# http://wrapper.tanukisoftware.com/trial

# Include file problems can be debugged by removing the first '#'
#  from the following line:
##include.debug

# The Wrapper will look for either of the following optional files for a
#  valid License Key.  License Key properties can optionally be included
#  directly in this configuration file.
#include ./wrapper-license.conf

# The following property will output information about which License Key(s)
#  are being found, and can aid in resolving any licensing problems.
#wrapper.license.debug=TRUE

#********************************************************************
# Wrapper Localization
#********************************************************************
# Specify the locale which the Wrapper should use.  By default the system
#  locale is used.
#wrapper.lang=en_US # en_US or ja_JP

# Specify the location of the Wrapper's language resources.  If these are
#  missing, the Wrapper will default to the en_US locale.
wrapper.lang.folder=../lang

#********************************************************************
# Wrapper Java Properties
#********************************************************************
# Java Application
#  Locate the java binary on the system PATH:
#  Specify a specific java binary:
set.JAVA_HOME=lib/runtime/jre-win
wrapper.java.command=%JAVA_HOME%/bin/java

# Tell the Wrapper to log the full generated Java command line.
#wrapper.java.command.loglevel=INFO

# Java Main class.  This class must implement the WrapperListener interface
#  or guarantee that the WrapperManager class is initialized.  Helper
#  classes are provided to do this for you.  See the Integration section
#  of the documentation for details.
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp

# Java Classpath (include wrapper.jar)  Add class path elements as
#  needed starting from 1
wrapper.java.classpath.1=lib/wrapper.jar
wrapper.java.classpath.2=lib/core/common/*
wrapper.java.classpath.3=lib/core/gateway/*

# Java Library Path (location of Wrapper.DLL or libwrapper.so)
wrapper.java.library.path.1=lib

# Java Bits.  On applicable platforms, tells the JVM to run in 32 or 64-bit mode.
#wrapper.java.additional.auto_bits=TRUE
wrapper.java.additional.auto_bits.macosx=FALSE
wrapper.java.additional.auto_bits.macosx=FALSE

# Java Additional Parameters
#wrapper.java.additional.1=-Ddata.dir=data
#wrapper.java.additional.2=-Xdebug
#wrapper.java.additional.3=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:8000
wrapper.java.additional.1=-XX:MaxGCPauseMillis=100
wrapper.java.additional.2=-Ddata.dir=data
wrapper.java.additional.3=-Dorg.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false


# Initial Java Heap Size (in MB)
wrapper.java.initmemory=1024

# Maximum Java Heap Size (in MB)
wrapper.java.maxmemory=4096

# Application parameters.  Add parameters as needed starting from 1
wrapper.app.parameter.1=com.inductiveautomation.catapult.Catapult

wrapper.shutdown.timeout=120
wrapper.jvm_exit.timeout=120

# Set this value to TRUE to allow ignition.conf to be reloaded when the JVM is restarted
wrapper.restart.reload_configuration=TRUE

#********************************************************************
# Wrapper Logging Properties
#********************************************************************
# Enables Debug output from the Wrapper.
# wrapper.debug=TRUE

# Format of output for the console.  (See docs for formats)
wrapper.console.format=PM

# Log Level for console output.  (See docs for log levels)
wrapper.console.loglevel=INFO

# Log file to use for wrapper output logging.
wrapper.logfile=logs/wrapper.log

# Format of output for the log file.  (See docs for formats)
wrapper.logfile.format=LPTM

# Log Level for log file output.  (See docs for log levels)
wrapper.logfile.loglevel=INFO

# Maximum size that the log file will be allowed to grow to before
#  the log is rolled. Size is specified in bytes.  The default value
#  of 0, disables log rolling.  May abbreviate with the 'k' (kb) or
#  'm' (mb) suffix.  For example: 10m = 10 megabytes.
wrapper.logfile.maxsize=10m

# Maximum number of rolled log files which will be allowed before old
#  files are deleted.  The default value of 0 implies no limit.
wrapper.logfile.maxfiles=5

# Log Level for sys/event log output.  (See docs for log levels)
wrapper.syslog.loglevel=NONE

#********************************************************************
# Wrapper General Properties
#********************************************************************
# Allow for the use of non-contiguous numbered properties
wrapper.ignore_sequence_gaps=TRUE

# Title to use when running as a console
wrapper.console.title=Ignition Gateway

#********************************************************************
# Wrapper JVM Checks
#********************************************************************
# Detect DeadLocked Threads in the JVM. (Requires Standard Edition)
#wrapper.check.deadlock=TRUE
#wrapper.check.deadlock.interval=60
#wrapper.check.deadlock.action=RESTART
#wrapper.check.deadlock.output=FULL

# Out Of Memory detection.
#wrapper.filter.trigger.1000=java.lang.OutOfMemoryError
#wrapper.filter.action.1000=RESTART
#wrapper.filter.message.1000=The JVM has run out of memory.

wrapper.on_exit.default=SHUTDOWN
wrapper.on_exit.2=RESTART

#********************************************************************
# Wrapper Email Notifications. (Requires Professional Edition)
#********************************************************************
# Common Event Email settings.
#wrapper.event.default.email.debug=TRUE
#wrapper.event.default.email.smtp.host=<SMTP_Host>
#wrapper.event.default.email.smtp.port=25
#wrapper.event.default.email.subject=[%WRAPPER_HOSTNAME%:%WRAPPER_NAME%:%WRAPPER_EVENT_NAME%] Event Notification
#wrapper.event.default.email.sender=<Sender email>
#wrapper.event.default.email.recipient=<Recipient email>

# Configure the log attached to event emails.
#wrapper.event.default.email.attach_log=TRUE
#wrapper.event.default.email.maillog.lines=50
#wrapper.event.default.email.maillog.format=LPTM
#wrapper.event.default.email.maillog.loglevel=INFO

# Enable specific event emails.
#wrapper.event.wrapper_start.email=TRUE
#wrapper.event.jvm_prelaunch.email=TRUE
#wrapper.event.jvm_start.email=TRUE
#wrapper.event.jvm_started.email=TRUE
#wrapper.event.jvm_deadlock.email=TRUE
#wrapper.event.jvm_stop.email=TRUE
#wrapper.event.jvm_stopped.email=TRUE
#wrapper.event.jvm_restart.email=TRUE
#wrapper.event.jvm_failed_invocation.email=TRUE
#wrapper.event.jvm_max_failed_invocations.email=TRUE
#wrapper.event.jvm_kill.email=TRUE
#wrapper.event.jvm_killed.email=TRUE
#wrapper.event.jvm_unexpected_exit.email=TRUE
#wrapper.event.wrapper_stop.email=TRUE

# Specify custom mail content
wrapper.event.jvm_restart.email.body=The JVM was restarted.\n\nPlease check on its status.\n

#********************************************************************
# Wrapper Windows NT/2000/XP Service Properties
#********************************************************************
# WARNING - Do not modify any of these properties when an application
#  using this configuration file has been installed as a service.
#  Please uninstall the service before modifying this section.  The
#  service can then be reinstalled.

# Name of the service
wrapper.name=Ignition

# Display name of the service
wrapper.displayname=Ignition Gateway

# Description of the service
wrapper.description=Ignition Gateway

# Service dependencies.  Add dependencies as needed starting from 1
wrapper.ntservice.dependency.1=

# Mode in which the service is installed.  AUTO_START, DELAY_START or DEMAND_START
wrapper.ntservice.starttype=AUTO_START

# Allow the service to interact with the desktop.
wrapper.ntservice.interactive=false

#VERSION - DO NOT MODIFY: 13


One of our defect analysts is going to try to reproduce this and see if it’s a memory leak in Vision.

Sounds good. Thank you!

Any news?

Can you disable caching on the image component? The images aren’t being stored in the cache due to the script anymore, but instead are being retained from the image component. Make sure to still keep the loadImageNoCache method within your script.

We’ll look into implementing some pruning mechanism to cleanup the cache, but for now, you can either bypass cache altogether or periodically call resetCache().

1 Like

Thank you Kurt.

The solution worked.

In the image component´s Property Editor, I set Use Cache to False while using loadImageNoCache(filename, None). The memory issue was resolved.