Java Memory Leaks et al. (2. Act)

本文详细探讨了Java应用程序中出现java.lang.OutOfMemoryError的原因,包括内存泄漏、过多或过大的对象消耗内存以及大量临时对象导致的问题,并介绍了如何通过理解垃圾回收机制来避免这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The first act of this blog-series Java OutOfMemoryError – A tragedy in seven acts described the architecture of JVM memory and discussed where a java.lang.OutOfMemoryError could occur.

So let’s have a detailed look on how this error can actually happen in a Java application.

In the previous post, we saw multiple possible types of the OutOfMemoryError. But the one happening most of the time is this one:

Exception in thread "main": java.lang.OutOfMemoryError: Java heap space

The error indicates that there has not been enough free heap to fulfill an allocation request for a new object. In other words: there is no room for the object on the heap. Since, according to the JVM specification, every heap has to have a garbage collector, this also means that no memory was freeable by it. So the whole memory is filled with “live” objects.

To understand how this can happen, it is important to understand what a “live” object actually is.

In Java objects get generated on the heap and live on as long as they are referenced. The garbage collector checks during it GC-phases if the object is still referenced – and does mark it as “garbage” and clean it up later when this is no longer the case (the actual behaviour differs for different gc-algorithms, like the Copying Collectors or Garbage-First, but is not of importance for understanding liveness). But not every reference is important for surviving, but only so called GC root references. Especially when understanding Memory Leaks, GC roots are a central concept to identify the important references to an object. Garbage Collector Roots are special objects which do not have incoming references and are responsible for keeping referenced objects alive. When an object cannot be reached directly or indirectly from a GC root, it will be marked as “unreachable” and made eligible for garbage collection. Simply speaking, there are three kinds of GC roots:

• Temporary variables on the stack of a thread

• Static members of a loaded class

• Special native references from JNI

Here an example which shows where GC Roots play a role for a class:

public class MyFrame extends javax.swing.JFrame {
 
  // reachable via Classloader as soon class is loaded
  public static final ArrayList STATIC = new ArrayList();
 
  // as long as the JFrame is not dispose()'d,
  // it is reachable via a native window
  private final ArrayList jni = new ArrayList()
 
  // while this method is executing parameter is kept reachable,
  // even if it is not used
  private void myMethod(ArrayList parameter) {
 
    // while this method is running, this list is reachable from the stack
    ArrayList local = new ArrayList();
  }
 
}

Usually there are tree types of issues with Java OutOfMemoryError problems in heap memory:

  • Objects, which can be reached via a GC root reference, but are actually no longer used by application code. Those are called Java Memory Leaks.
  • Too many or too large objects. So there is not enough heap available for the application to execute. Usually happens when there are large objects kept in cache like structures.
  • Too many temporary objects. So there is just for a short time not enough memory. Usually happens in load scenarios where many temporary objects are used.

Java Memory Leaks

So Java Memory Leaks occur when objects still have a GC root reference, but are not actually used anymore. Those “Loitering Objects” stay around for the whole life of the JVM. If the application is creating those “dead objects” on and on, the memory will be filled up and eventually result in a java.lang.OutOfMemoryError. Typical causes are static collections, which are used as a kind of cache. Usually objects are added, but never removed (Let’s face it: How often have you used add() and put() and how often used remove() methods?). Because the objects are referenced by the static collection, they cannot be freed up anymore, as the collection has a GC root reference via the classloader.

When discussing Memory Leaks one comes usually across the terms dominator or dominator tree. The dominator concept comes from graph theory and defines a node as dominator of another node when this node can only be reached via it. Applying this to memory management, object A is dominator for object B when there is no object C which holds a reference to B. A dominator tree is a partial tree in which this condition holds true for all nodes from the root node. When the root reference is freed, the whole dominator tree is freed as well. Large dominator trees are great candidates when looking for memory leaks.

Depending on the creation frequency and the object size, as well as the size of the Java heap, the OutOfMemoryError occurs sooner or later. Especially those “creeping memory leaks” can be found in many applications, but are usually “ignored” by:

  • Using large heaps to delay the error. Happens frequently nowadays, as the old 32bit memory limit has vanished through the usage of 64 bit JVMs.
  • Restarting the application server during the night. This “resets” the memory usage. If the memory leak takes longer than 24 hours to become severe, this will help.

But both variants are dangerous, as they have negative impact on the system performance and are heavily influenced by the system usage. A change in usage or more “traffic” can produce the error faster than expected. Garbage collection times also have a negative effect on the application performance, as the increasing “tenured generation” causes longer “Mark”-Phases during garbage collection, resulting in longer pause times, which can be observed as system hangs. Act 3 and 4 will describe analysis of those leaks in details and give advice on how to avoid them.

Too much memory

Besides Java memory leaks, there are is another reason for OutOfMemoryError: The application is just consuming too much memory. Either there is not enough heap configured and it has to be increased (also see part 3 of the series) or the consumption has to be throtteled, e.g. by shrinking cache sizes.

Especially critical is high temporary memory usage in enterprise applications, which can have a high number of concurrent users. Because it can happen out of the blue, this OutOfMemoryError is especially troublesome, as it cannot be countered with a nightly reboot. The following code illustrates the problem:

byte[] image = getTheByteImage();
response.setContentType("image/jpeg");
ServletOutputStream out = response.getOutputStream();
out.write(image);
out.flush();
out.close();

While it is not that obvious, the code consumes memory on heap for each image before sending it to the browser. A much better variant would be to stream the image like this:

InputStream image = getTheImageAsStream();
response.setContentType("image/jpeg");
ServletOutputStream out = response.getOutputStream();
IOUtils.copy(image, out);
out.flush();
out.close();

(Of course BufferedStreams and IOUtils use byte[] internally as well, but this is much smaller)

Having covered only the java.lang.OutOfMemoryError problems in heap, I might dedicate another post to other areas, like the permanent generation as mentioned in the previous episode.

The next episode will be “Confguring and Monitoring the Java Virtual Machine”, which will show how to configure and optimize the Sun JVM and how to monitor it with the bundled tools.

“Creating and understanding Java Heapdumps“ will then be the fourth episode and describe how to handle Heapdumps. We will find out how to discover the causes of the Memory Leaks described here.

Also those two are going to be more practical oriented, so you can expect some screencasts.

资源下载链接为: https://pan.quark.cn/s/d9ef5828b597 四路20秒声光显示计分抢答器Multisim14仿真源文件+设计文档资料摘要 数字抢答器由主体电路与扩展电路组成。优先编码电路、锁存器、译码电路将参赛队的输入信号在显示器上输出;用控制电路和主持人开关启动报警电路,以上两部分组成主体电路。通过定时电路和译码电路将秒脉冲产生的信号在显示器上输出实现计时功能,构成扩展电路。经过布线、焊接、调试等工作后数字抢答器成形。关键字:开关阵列电路;触发锁存电路;解锁电路;编码电路;显示电路 一、设计目的 本设计是利用已学过的数电知识,设计的4人抢答器。(1)重温自己已学过的数电知识;(2)掌握数字集成电路的设计方法和原理;(3)通过完成该设计任务掌握实际问题的逻辑分析,学会对实际问题进行逻辑状态分配、化简;(4)掌握数字电路各部分电路与总体电路的设计、调试、模拟仿真方法。 二、整体设计 (一)设计任务与要求: 抢答器同时供4名选手或4个代表队比赛,分别用4个按钮S0 ~ S3表示。 设置一个系统清除和抢答控制开关S,该开关由主持人控制。 抢答器具有锁存与显示功能。即选手按动按钮,锁存相应的编号,并在LED数码管上显示,同时扬声器发出报警声响提示。选手抢答实行优先锁存,优先抢答选手的编号一直保持到主持人将系统清除为止。 参赛选手在设定的时间内进行抢答,抢答有效,定时器停止工作,显示器上显示选手的编号和抢答的时间,并保持到主持人将系统清除为止。 如果定时时间已到,无人抢答,本次抢答无效。 (二)设计原理与参考电路 抢答器的组成框图如下图所示。它主要由开关阵列电路、触发锁存电路、解锁电路、编码电路和显示电路等几部分组成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值