java string 内存泄漏_Java内存泄漏

超、凢脫俗 2022-11-03 11:28 471阅读 0赞

Java内存泄漏

解释

内存泄漏: 存在一些被分配的对象,满足两个特点:

对象是可达的:在有向图中,存在通路与之相连.

对象是无用的:程序以后不再使用这些对象.

结果:消耗越来越多的内存资源,最终导致OutOfMemoryError。

与C++的区别:

C++:对象被分配内存空间,却不可达.

Java:不可达的对象由GC回收.

示例:

Vector v = new Vector(10);

for(int i = 0; i < 10;i++){

Object o = new Object();

v.add(o);

o = null;

}

对象可以分为两种:有引用的对象和无引用的对象。

其中无引用的对象由GC进行回收。

有引用的对象不会被GC回收,即使这些对象不会再被使用。

b3ab5af84cf0fb6aa2583ca6670b77ee.png

Java堆泄漏

注:通过-Xms和-Xmx设置堆的最小值和最大值,使得容易的重现堆泄漏。

几种常见的泄漏情景:

存储对象引用的静态域

示例:

// JVM参数设定:-Xms10m -Xmx10m

// 会出现OutOfMemoryError

private Random random = new Random();

public static final ArrayList list = new ArrayList<>(1000000);

@Test

public void test() throws InterruptedException {

for (int i = 0; i < 1000000; i++) { // 向static域加入对象

list.add(random.nextDouble());

}

System.gc();

Thread.sleep(10000); // 使得GC得以执行

}

使用非static域存放对象:

// 不会出现OutOfMemoryError

@Test

public void test() throws InterruptedException {

addelement();

System.gc();

Thread.sleep(10000);

}

private void addelement(){

ArrayList list = new ArrayList<>(1000000); // 使用非static域存储对象

for (int i = 0; i < 1000000; i++) {

list.add(random.nextDouble());

}

}

解决对策:

谨慎使用static变量,其生命周期与JVM的生命周期相同。

谨慎使用集合类,其常常存储大量不会再被使用到的对象引用。

对长字符串使用String.intern()

示例:

@Test

public void test() throws InterruptedException, FileNotFoundException {

Thread.sleep(15000);

String str = new Scanner(new File(“scr/test.txt”),”UTF-8”).useDelimiter(“\\A”).next();

str.intern(); // 向字符串常量池中加入长字符串

System.gc();

Thread.sleep(16000);

}

解决对策:

String.intern()方法将字符串存储在永久代空间中,若需要将大量长字符串存入字符串常量池,可通过最大永久代大小来避免异常:-XX:MaxPermSize=。

使用Java 8:其中永久代被元空间取代,使用String.intern()不会导致异常。

未关闭的流

// 流在使用后没有被关闭

@Test

public void test() throws IOException {

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();

}

注:未关闭的流会导致两种类型的泄漏

底层资源泄漏:文件描述符(file descriptors),打开的连接(open connections)。

JVM内存泄漏

解决对策:

使用完流后手动关闭。

使用Java 8中的自动关闭特性。

// 无需在finally中手动关闭

try (BufferedReader br = new BufferedReader(

new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {

// …

} catch (IOException e) {

e.printStackTrace();

}

未关闭的连接

与未关闭的流类似。

一般是连接数据库或FTP后未关闭。

// 连接FTP后未关闭

@Test

public void test() throws IOException {

URL url = new URL(“ftp://speedtest.tele2.net”);

URLConnection urlc = url.openConnection();

InputStream is = urlc.getInputStream();

String str = “ “;

}

解决对策:

使用完连接后手动将连接关闭。

将没有hashCode()和equal()的对象加入到Hashset中

当一个对象没有重写hashCode()和equals()方法时,在向HashSet集合类中添加重复的对象时,其不会忽略到重复的对象。

@Test

public void test(){

Map map = System.getProperties();

while (true){

map.put(new newObject(“key”),”value”);

}

}

// 没有重写equals和hashCode的自定义类

class newObject{

private String key;

public newObject(String key){

this.key = key;

}

}

解决方法

重写equals()和hashCode()方法。

或者使用插件lombok。

查找泄漏源

打开垃圾收集器的详细日志信息。JVM参数中添加-verbose:gc。

使用监控工具(Visual VM)。

检查代码。

参考:

发表评论

表情:
评论列表 (有 0 条评论,471人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java内存泄漏

    Java内存管理机制 在C++ 语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期。从申请分配、到使用、再到最后的释放。这样的过程非常灵活,但是却十分繁琐

    相关 Java内存泄漏

    问题的提出 Java的一个重要优点就是通过垃圾收集器(Garbage Collection,GC)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为

    相关 java内存泄漏

    1、Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址。 Java中对象是采用new或者反射的方法创建的,