In this series of tutorials, you'll take a look at the mysteries of Java memory leaks and teach the reader relevant analytical methods. Here is a case.
Recently, there is a server, often running when there is an overload of the phenomenon of downtime. This issue still occurs after you restart the script and the system. Despite the large amount of data loss, the problem is not serious because it is not a critical business. However, it was decided to make a further investigation to see where the problem actually arose. The first thing to notice is that the server passes all the unit tests and tests for the complete integration environment. When working with test data in a test environment, why is there a problem running in a production environment? It is easy to think that the actual run-time load is larger than the load at the time of the test, or even the load of the design, which depletes the resources. But what is the resource and where is it exhausted? Let's look at the problem.
To illustrate the problem, the first thing to do is to write some memory leak code that will be implemented using the production-consumer model to better illustrate the problem.
example, suppose you have a scenario where you work for a securities brokerage firm that records sales and shares of shares in a database. Get the command from a simple process and store it in a queue. Another process reads the command from the queue and writes it to the database. The Pojo object of the command is very simple, as shown in the following code:
public class Order { private final int id; private final String code; Private F inal int amount; private final double price; private final long time; Private final long[] padding; /** * @param ID * & nbsp The Order ID * @param code * The Stock code * @param amount * the number of shares * @param price * & nbsp; the price of the share * @param time *     &NB sp; the transaction time * * public Order (int ID, String code, int amount , double price, LOng time) { super (); this.id = id; this.code = code; This.amount = amount; this.price = Price; this.time = time; //Here deliberately set the order object to be large enough to facilitate an example to run out of memory at a later time this.padding = new Long [3000]; arrays.fill (padding, 0, padding.length-1,-2); } public int getId () { return ID, } public String GetCode () { return code; } public int getamount () { return amount, } public double get Price () { return price, } public long getTime () { return time; } }
This Pojo object is part of the spring application, which has three main abstract classes, and when spring calls their start () method, it creates a new thread, respectively.
The first abstract class is orderfeed. The run () method generates a series of random order objects and places them in the queue, then it sleeps for a while and then generates a new order object with the following code:
public class Orderfeed implements Runnable { private static random rand = new Random (); private St atic int id = 0; private final blockingqueue<order> Orderqueue; public orderfeed (blockingqueue<order> orderqueue) { this.orderqueue = OrderQueue; } /** * Called by spring after loading context contexts, start production order object */ public void start () { & nbsp Thread thread = new Thread (this, "Order producer"); Thread.Start (); } @Override public void Run () { while (true) { Order Order = Createorder (); Orderqueue.add (order); sleep (); } private Order Createorder () { final string[] stocks = {"Blnd. L "," DGE. L "," MKS. L "," Pson. L "," RIO. L "," PRU. L ", " LSE. L "," WMH. L "}; int next = RanD.nextint (stocks.length); Long now = System.currenttimemillis (); Order order = New Order (++id, Stocks[next], next *, Next *, now); return order; } private void Sleep () { try { TimeUnit.MILLISECONDS.sleep (100) ; catch (Interruptedexception e) { e.printstacktrace (); } }
The
Second class is Orderrecord, which is responsible for extracting order objects from the queue and writing them to the database. The problem is that it takes much longer to write an order object to the database than to produce an order object. For demonstration purposes, it will sleep for 1 seconds in the Recordorder () method.
public class Orderrecord implements Runnable { private final blockingqueue<order> orderqueue; &n Bsp Public Orderrecord (blockingqueue<order> orderqueue) { this.orderqueue = orderqueue; } & nbsp; public void Start () { thread thread = new Thread (this, "Order Recorder");   ; Thread.Start (); } @Override public void Run () { while (true) {   ; try { Order order = Orderqueue.take (); Recordorder (order); catch (Interruptedexception e) { E.printstacktrace (); } } } /** * Simulate logging to a database method, Here's just a simple one-second sleep. */ public void Recordorder (order order) throws interruptedexception { TimeUnit.SECONDS.sleep (1); } }
In order to prove this effect, deliberately added a monitoring class orderqueuemonitor, the class every few seconds to print out the size of the queue, the code is as follows:
public class Orderqueuemonitor implements Runnable { private final blockingqueue<order> Orderqueue; public Orderqueuemonitor (blockingqueue<order> orderqueue) { This.orderqueue = Orderqueue; } public void Start () { thread thread = new Thread (this, "Order Queue Monitor "); Thread.Start (); } @Override public void Run () { while (true) { ; try { TimeUnit.SECONDS.sleep (2); int size = Orderqueue.size (); System.out.println ("Queue size is:" + size); catch (Interruptedexception e) { E.printstacktrace (); } } } }
Configure the following configuration file for the Spring framework:
<?xml version= "1.0" encoding= "UTF-8"?> <beans xmlns= "Http://www.springframework.org/schema/beans" xmlns:p= "http://www.springframework.org/schema/p" xmlns:xsi= "Http://www.w3.org/2001/XMLSchema-instance" xmlns: context= "Http://www.springframework.org/schema/context" xsi:schemalocation= "http://www.springframework.org/ Schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ Context http://www.springframework.org/schema/context/spring-context-3.1.xsd "default-init-method=" Start " Default-destroy-method= "Destroy" > <bean id= "Thequeue" class= "Java.util.concurrent.LinkedBlockingQueue "/> <bean id=" Orderproducer "> <constructor-arg ref=" thequeue "/> </bean> <bean id= "Orderrecorder" > <constructor-arg ref= "thequeue"/> </bean> <bean id= "Queuemonitor" > < Constructor-arg ref= "Thequeue"/> </bean> </beans>
The spring application is then run, and the memory of the application can be monitored by jconsole, which requires some configuration, as follows:
If you look at the heap usage, you will find that as the queue increases, the heap usage increases gradually, as shown, you may not find a 1KB memory leak, but it is obvious when the memory overflow reaches 1GB. So the next thing to do is to wait for it to overflow and then analyze it.
Let's take a look at how to find and solve this kind of problem. In Java, there are a number of self-contained or third-party tools available to help us with the relevant analysis.
The following is a three-step procedure for analyzing a memory leak problem:
- Extracts a dump file for a server that has a memory leak.
- Generate a report with this dump file.
- Analyze the generated reports.
There are several tools that can help you to make a heap of dump files, respectively:
- Jconsole
- Visualvm
- Eclipse Memory Analyser Tool (MAT)
Extracting heap dump files with Jconsole
Connect to your app using Jconsole: Click the Mbeans tab to open the Com.sun.management package, click Hotspotdiagnostic, click Operations, and then select Dumpheap. At this point you will see the dumpheap operation: it accepts two parameters P0 and P1. Enter a file name for the heap dump in the P0 edit box, and then press the Dumpheap button. Such as:
Extracting heap dump files with JVISUALVM
First use the Jvisual VM to connect the sample code, then right-click on the app and select Heap Dump in the Application pane on the left.
Note: If the memory leak that needs to be analyzed is on the remote server, then JVISUALVM will save the file that was dumped in the/tmp directory on the remote machine (assuming this is a UNIX machine).
Use mat to extract heap dump files
Jconsole and JVISUALVM are themselves part of the JDK, and mat or called "Memory Analyzer" is an eclipse-based plugin that can be downloaded from eclipse.org.
The latest version of MAT requires you to install JDk1.6 on your computer. Don't worry if you're using the Java1.7 version, as it will automatically install version 1.6 for you and will not clash with the installed version 1.7.
When using the mat, just click on "Aquire Heap Dump" and then follow the steps to do so, such as:
Note that using the three methods above, you need to configure the remote JMX connection as follows:
When to extract heap dump files
So when should I extract the heap dump files? It takes a bit of thought and luck. If you extract the heap dump files prematurely, you will not be able to find the crux of the problem because they are masked by legitimate, non-leaking instances of the class. However, you cannot wait too long because extracting a heap dump file also requires memory, which can cause the application to crash when it is extracted.
The best way to do this is to connect the Jconsole to the application and monitor the heap occupancy, knowing when it is at the edge of the crash. Since there is no memory leak, the three-heap portion of the metrics is green, which is easy to monitor, such as:
Analyzing the dump file
Now it's time for the mat to come in handy, because it's itself designed to analyze heap dump files. To open and parse a heap dump file, you can choose the heap dump option for the File menu. Once you have selected the file you want to open, you will see the following three options:
Select the Leak Suspect report option. After the mat runs for a few seconds, the page is generated as follows:
As the pie chart shows, there is a suspected memory leak in one place. You might think that this approach is preferable only if the code is under control. After all, this is just an example, what does this mean? Well, in this case, all the problems are simple and easy to see, thread A takes up 98.7MB of memory, and the other threads use 1.5MB. In the actual situation, the resulting chart may be that way. Let's continue to explore and get as follows:
As shown, the next section of the report tells us that a linkedblockqueue occupies 98.46% of the memory. To further explore, click on details>>, such as:
As you can see, the problem really is on our orderqueue. This queue stores all generated, randomly generated order objects and can be accessed by the three threads Orderfeed, Orderrecord, and Ordermonitor mentioned in our previous blog post.
So everything is clear, Mat tells us: There is a linkedblockqueue in the sample code that runs out of all memory, causing serious problems. But we don't know why this problem arises, and we can't expect the mat to tell us.
This code can be downloaded in: Https://github.com/roghughe/captaindebug/tree/master/producer-consumer.
Original link: http://www.javacodegeeks.com/2013/12/investigating-memory-leaks-part-1-writing-leaky-code.html
Explore Java memory leak issues