CopyOnWriteArrayList内存占用过多

编程入门 行业动态 更新时间:2024-10-26 15:12:57

CopyOnWriteArrayList<a href=https://www.elefans.com/category/jswz/34/1771154.html style=内存占用过多"/>

CopyOnWriteArrayList内存占用过多

目录

    • 一、CopyOnWriteArrayList
    • 二、CopyOnWriteArrayList的适用场景
    • 三、CopyOnWriteArrayList内存占用过多的解决方法
    • 四、CopyOnWriteArrayList.add()源码分析

大家好,我是哪吒。

一、CopyOnWriteArrayList

CopyOnWriteArrayList是Java中的一个线程安全的ArrayList,它在每次修改时都会创建一个新的数组,并将原数组的引用赋值给新数组,从而达到线程安全的目的。由于它不断地创建新的数组并保留旧数组,因此内存占用可能会比较多。

CopyOnWriteArrayList具有以下特点:

  1. 线程安全:CopyOnWriteArrayList使用读写锁来确保并发访问时的线程安全性,因此可以在多线程环境下使用。
  2. 内存占用较高:由于它在每次修改操作时都会创建一个新的数组,并且会保留旧数组,因此内存占用可能会比较多。
  3. 适合读密集型应用:由于它只会在写操作时进行数组复制,因此对于读密集型的应用,使用CopyOnWriteArrayList可以减少锁竞争,提高并发性能。
  4. 不支持写操作:CopyOnWriteArrayList不支持add()和remove()方法,因为这些操作需要修改底层数组,从而导致所有线程都看到不一致的结果。因此,如果需要执行写操作,可以考虑使用其他的线程安全的数据结构,如Collections.synchronizedList()。

总之,CopyOnWriteArrayList适合读密集型应用,并且在写操作时需要注意内存占用问题。如果需要执行写操作,可以考虑使用其他的线程安全的数据结构。

二、CopyOnWriteArrayList的适用场景

CopyOnWriteArrayList主要适用于读多写少的场景,例如配置信息、缓存等数据的读取和更新操作。由于写操作的时候会进行数组复制,会消耗内存,如果原数组的内容比较多的情况下可能导致young gc或者full gc。因此,对于读操作非常频繁,而写操作相对较少的场景,使用CopyOnWriteArrayList可以提供较好的并发性能和线程安全性。

需要注意的是,CopyOnWriteArrayList在写操作中使用了ReentrantLock锁来保证线程安全,并替换原array属性;但是在读的时候直接读取array,可能会发生在写操作替换array前后,从而引发数据不一致的问题。因此,在使用CopyOnWriteArrayList时需要注意以下几点:

  1. 如果写操作未完成,那么直接读取原数组的数据是不一致的。
  2. 如果写操作完成,但是引用还未指向新数组,那么也是读取原数组数据是不一致的。
  3. 如果写操作完成,并且引用已经指向了新的数组,那么直接从新数组中读取数据是一致的。

总之,CopyOnWriteArrayList适用于读多写少且数据一致性要求较高的场景。

三、CopyOnWriteArrayList内存占用过多的解决方法

  1. 尽量避免在CopyOnWriteArrayList中进行频繁的修改操作。如果可能的话,可以尝试将这些修改操作集中在一起进行,从而减少数组的创建和销毁次数。
  2. 使用其他的线程安全的数据结构,比如Collections.synchronizedList()。这种数据结构使用的内存开销通常比CopyOnWriteArrayList小。
  3. 如果必须要使用CopyOnWriteArrayList,可以使用WeakReference来减少内存占用。具体来说,可以将数组元素使用WeakReference管理,当数组不再被引用时,垃圾回收器可以将其回收,从而释放内存。

另外,你还可以通过查看JVM的内存快照(比如使用jstat工具)来分析内存占用过多的原因。这可以帮助你更好地了解内存占用情况并采取相应的措施。

四、CopyOnWriteArrayList.add()源码分析

public boolean add(E e) {// 获取独占锁final ReentrantLock lock = this.lock;lock.lock();try {// 获取arrayObject[] elements = getArray();int len = elements.length;// 复制array到新数组,添加元素到新数组Object[] newElements = Arrays.copyOf(elements, len + 1);newElements[len] = e;// 替换数组setArray(newElements);return true;} finally {// 释放锁lock.unlock();}
}

CopyOnWriteArrayList内部定义了一个内部array数组,然后复制array到新数组,添加元素到新数组。所有的读操作都是基于新的array对象进行的。

private transient volatile Object[] array;final Object[] getArray() {return array;
}

因为上了独占锁ReentrantLock,如果多个线程调用add()方法,只会有一个线程会获得到该锁,其他线程被阻塞,直至锁被释放, 由于加了锁,所以整个操作的过程是原子性操作。

由于每次写入的时候都会对数组对象进行复制,复制过程不仅会占用双倍内存,还需要消耗 CPU 等资源,如果要保存大量元素,并放任其成长,内存和CPU将面临重大考验,场面堪比金角巨兽,后果不堪设想。

对 CopyOnWriteArrayList 每一次修改,都会重新创建一个大对象,并且原来的大对象也需要回收,这都可能会触发 GC,如果超过老年代的大小则容易触发Full GC,引起应用程序长时间停顿。

🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

更多推荐

CopyOnWriteArrayList内存占用过多

本文发布于:2023-11-15 02:21:29,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1592037.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:内存   CopyOnWriteArrayList

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!