当前位置:网站首页>How to avoid JVM memory leakage?
How to avoid JVM memory leakage?
2022-07-04 05:57:00 【Shockang】
Preface
This article belongs to the column 《100 One problem solved Java virtual machine 》, This column was originally written by the author , Please indicate the source of the quotation , Please point out the shortcomings and mistakes in the comment area , thank you !
For the table of contents and references of this column, please refer to 100 One problem solved Java virtual machine
brief introduction
Java One of our core strengths is JVM, This is an out of the box memory management .
essentially , We can create objects ,Java The garbage collector will be responsible for allocating and freeing memory for us .
For all that ,Java Memory leaks can still occur in applications .
In this paper , We will discuss the most common memory leaks , Understand the reason , And study some tests / Technology to avoid them .
We will also use throughout this article Java YourKit Analyzer to analyze the memory state of our runtime .
1. Java What is the memory leak in ?
The standard definition of a memory leak is when an application no longer uses objects , But what happens when the garbage collector cannot delete them from the working memory —— Because they are still being quoted .
therefore , This application consumes more and more resources —— This eventually leads to fatal OutOfMemoryError
.
To better understand the concept , Here is a simple visual representation Java How memory leaks occur in :
As we can see , We have two types of objects —— Referenced objects and unreferenced objects ; The garbage collector can delete unreferenced objects , Referenced objects will not be collected , Even if applications actually no longer use them .
Detecting memory leaks can be difficult , Many tools perform static analysis to identify potential leaks , But these technologies are not perfect , Because the most important aspect is the actual runtime behavior of the running system .
therefore , Let's analyze some common scenarios , Focus on some standard practices to prevent memory leakage .
2. Java Heap memory leak
In this part , We will focus on the classic memory leak scenario —— namely Constantly create Java Object without being released .
An advantageous technique to understand these situations is to make it easier to reproduce memory leaks by setting a lower heap size .
That's why when you start an application , We can adjust JVM To meet our memory needs :
-Xms<size>
-Xmx<size>
These parameters specify the initial Java Heap size and maximum heap size .
2.1 Keep static fields of object references
The first may lead to Java The memory leak scenario refers to a heavy object with static fields .
Let's look at a short example :
private Random random = new Random();
public static final ArrayList<Double> list = new ArrayList<Double>(1000000);
@Test
public void givenStaticField_whenLotsOfOperations_thenMemoryLeak() throws InterruptedException {
for (int i = 0; i < 1000000; i++) {
list.add(random.nextDouble());
}
System.gc();
Thread.sleep(10000); // allow GC Complete its work
}
We created ArrayList As a static field —— stay JVM Within the life cycle of the process ,JVM The garbage collector will never collect this field , Even after the calculation is completed .
We also call Thread.sleep(10000) To allow GC Complete execution , And try to recycle everything that can be recycled .
Let's run the test , And use our analyzer to analyze JVM:
Be careful , In the first place , Of course, all memory is free .
then , In a short span of 2 Seconds , The iteration process will run and complete —— Load everything into the list ( It naturally depends on the machine you run the test on ).
after , Trigger a complete garbage collection cycle , The test continues , To allow this cycle time to run and complete .
As you can see , The list is not recycled , Memory consumption will not decrease .
Now let's look at the exact same example , Just this time ,ArrayList Not referenced by static variables .
contrary , This is a created 、 Use and discard local variables :
@Test
public void givenNormalField_whenLotsOfOperations_thenGCWorksFine() throws InterruptedException {
addElementsToTheList();
System.gc();
Thread.sleep(10000); // allow GC Complete its work
}
private void addElementsToTheList(){
ArrayList<Double> list = new ArrayList<Double>(1000000);
for (int i = 0; i < 1000000; i++) {
list.add(random.nextDouble());
}
}
Once the method completes the work , We will observe GC The main collection of , About the 50 second :
How to avoid this problem ?
Now you know the scene , Of course, there are some ways to prevent it .
First , We need to Pay close attention static
Use ; Declare any collection or heavy object as static
, We need to integrate its life cycle with JVM Its own life cycle , This will make the whole object blood map unable to be collected .
We also need Be alert to the use of collections —— This may inadvertently keep references longer than we need .
2.2 Call on a long string String.intern()
The second group of scenarios that often lead to memory leaks involves String operation —— especially String.intern() API.
About String.intern() Please refer to my blog ——
Let's look at a short example :
@Test
public void givenLengthString_whenIntern_thenOutOfMemory()
throws IOException, InterruptedException {
Thread.sleep(15000);
String str = new Scanner(new File("src/test/resources/large.txt"), "UTF-8").useDelimiter("\\A").next();
str.intern();
System.gc();
Thread.sleep(15000);
}
ad locum , We just need to try to load a large text file into the running memory , And then use .intern() Return the form of the specification .
intern API Will put str String in JVM In the memory pool —— You can't collect it there —— Again , This will lead to GC Unable to free enough memory :
We can see clearly that , In the 15 second ,JVM It is stable. , Then we load the file ,JVM Perform garbage collection ( The first 20 second ).
Last , call str.intern(), This can lead to memory leaks —— A stable line indicating high heap memory utilization , And will never be released .
How to avoid this problem ?
please remember , Internal string objects are stored in PermGen In the space —— If our application is going to perform a lot of operations on large strings , We may need to increase the size of the permanent generation :
-XX:MaxPermSize=<size>
The second solution is to use Java 8—— among PermGen Space is replaced by meta space —— This is used on strings intern Will not lead to any OutOfMemoryError:
Last , There are also several ways to avoid .intern() API The option to .
2.3 Open stream
Forgetting to close the flow is a very common scenario , Of course , Most developers can relate to it .
When in try-with-resource When the function of automatically turning off all types of streams is introduced in Clause , The question is Java 7 Is partially eliminated .
Why is part of the reason ? Since the resource try syntax is optional :
@Test(expected = OutOfMemoryError.class)
public void givenURL_whenUnclosedStream_thenOutOfMemory()
throws IOException, URISyntaxException {
String str = "";
URLConnection conn
= new URL("http://norvig.com/big.txt").openConnection();
BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
while (br.readLine() != null) {
str += br.readLine();
}
}
Let's see from URL How about the memory of the application when loading large files :
As we can see , The use of heaps increases over time —— This is the direct effect of memory leakage caused by not closing the stream .
Let's study this scenario in more depth , Because it is not as clear as other scenes .
Technically speaking , Unclosed flow will cause two types of leakage —— Low level resource leakage and memory leakage .
Low level resource leakage is only the leakage of operating system level resources —— for example File descriptor 、 Open connection etc. , These resources may also leak , It's like memory .
Of course ,JVM Memory is also used to track these underlying resources , This is why this also leads to memory leaks .
How to avoid this problem ?
We always need to remember to close the flow manually , Or use Java 7 Automatic shutdown function introduced in :
try (BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
// further implementation
} catch (IOException e) {
e.printStackTrace();
}
under these circumstances ,BufferedReader Will be in try Automatically close at the end of the statement , Without explicit finally Close it in the block .
2.4 Unclosed connection
This situation is very similar to the previous scenario , The main difference is dealing with unclosed connections ( For example, database 、FTP The server etc. ).
Again , Improper implementation will cause a lot of harm , Cause memory problems .
Let's look at a short example :
@Test(expected = OutOfMemoryError.class)
public void givenConnection_whenUnclosed_thenOutOfMemory()
throws IOException, URISyntaxException {
URL url = new URL("ftp://speedtest.tele2.net");
URLConnection urlc = url.openConnection();
InputStream is = urlc.getInputStream();
String str = "";
}
URLConnection Keep it open , Predictably, , The result is a memory leak :
Please note that , The garbage collector cannot do anything to free unused but referenced memory . The first 1 Minutes later , The situation is obvious ——GC The number of operations decreases rapidly , This leads to an increase in heap memory usage , This leads to OutOfMemoryError.
How to avoid this problem ?
The answer here is simple —— We need to always establish connections in a standardized way .
2.5 stay HashSet Added in without rewriting hashCode() and equals() The object of
A simple but very common example that can lead to memory leaks is to HashSet And the lack of hashCode() or equals() Implemented objects .
say concretely , When we start adding duplicate objects to the collection , This will only increase , Instead of ignoring duplicate objects . Once added , We cannot delete these objects .
Let's create one without rewriting hashCode() and equals() The object of :
public class Key {
public String key;
public Key(String key) {
Key.key = key;
}
}
Now? , Let's look at the usage scenario :
@Test(expected = OutOfMemoryError.class)
public void givenMap_whenNoEqualsNoHashCodeMethods_thenOutOfMemory()
throws IOException, URISyntaxException {
Map<Object, Object> map = System.getProperties();
while (true) {
map.put(new Key("key"), "value");
}
}
This simple implementation will result in the following scenarios at runtime :
Notice how the garbage collector is 1:40 Stop reclaiming memory around , And pay attention to memory leakage ;GC The number of collections then fell nearly fourfold .
How to avoid this problem ?
under these circumstances , The solution is simple —— Provide hashCode() and equals() The implementation of is crucial .
One tool worth mentioning here is Project Lombok—— It provides many default implementations through annotations , for example @EqualsAndHashCode.
3. How to find the source of the leak in the application
Diagnosing memory leaks is a long process , It requires a lot of practical experience 、 Debugging skills and detailed application knowledge .
Let's look beyond standard analysis , What techniques can help you .
3.1 Detailed garbage collection printing
One of the fastest ways to identify memory leaks is to enable verbose garbage collection printing .
By way of -verbose:gc Parameters are added to our application JVM Configuration in progress , We will enable very detailed GC track .
The summary report is displayed in the default error output file , This should help us understand how memory is managed .
3.2 Profiling
The second technology is the one we use throughout this article —— That's it Profiling
. The most popular parser is Visual VM —— This is a start beyond the command line JDK Tools and get into a good place for lightweight analysis .
In this paper , We used another parser ——YourKit—— And Visual VM comparison , It has some extra 、 More advanced features .
3.3 review your code
Last , This is not so much a specific technology to deal with memory leaks , Rather, it is a good programming habit .
In short - Take a thorough look at your code , Practice regular code reviews , And make full use of static analysis tools to help you understand the code and system .
summary
In this paper , We actually studied JVM How does a memory leak happen on .
Understand how these scenarios happen , Is the first step in dealing with these situations .
then , As memory leaks occur , It is also crucial to have the technology and tools to really understand what is happening at runtime .
Static analysis and careful code review can only do so much , in the final analysis , The runtime will show us more complex memory leaks that cannot be immediately identified in the code .
Last , Memory leaks can be difficult to detect and replicate , Because many of these memory leaks only occur under dense loads , This usually happens in production environments .
ad locum , We need to go beyond code level analysis , And is committed to two main areas —— Recurrence and early detection .
The best and most reliable way to reproduce memory leaks is through a set of good performance tests , Simulate the usage mode of production environment as close as possible .
Early detection is a reliable performance management solution , Even early detection can have a relatively large impact , Because it may be the only means to have the necessary insight into the application runtime in the production environment .
边栏推荐
猜你喜欢
Configure cross compilation tool chain and environment variables
How does apscheduler set tasks not to be concurrent (that is, execute the next task after the first one)?
1480. Dynamic sum of one-dimensional array
ES6 模块化
注释与注解
Win10 clear quick access - leave no trace
如何避免 JVM 内存泄漏?
JS arguments parameter usage and explanation
Detailed explanation of common APIs for component and container containers: frame, panel, scrollpane
体验碎周报第 102 期(2022.7.4)
随机推荐
安装 Pytorch geometric
Halcon图片标定,使得后续图片处理过后变成与模板图片一样
卸载Google Drive 硬盘-必须退出程序才能卸载
JS execution mechanism
QT 获取随机颜色值设置label背景色 代码
js获取对象中嵌套的属性值
BUU-Pwn-test_ your_ nc
AWT introduction
Thinkphp6.0 middleware with limited access frequency think throttle
SQL injection - injection based on MSSQL (SQL Server)
我的NVIDIA开发者之旅——优化显卡性能
How does apscheduler set tasks not to be concurrent (that is, execute the next task after the first one)?
1480. Dynamic sum of one-dimensional array
Invalid revision: 3.18.1-g262b901-dirty
1.1 history of Statistics
Configure cross compilation tool chain and environment variables
Tf/pytorch/cafe-cv/nlp/ audio - practical demonstration of full ecosystem CPU deployment - Intel openvino tool suite course summary (Part 2)
VB. Net simple processing pictures, black and white (class library - 7)
Compound nonlinear feedback control (2)
Accidentally deleted the data file of Clickhouse, can it be restored?