Java笔记之改善扩容问题

2019-05-09 1

  

        在Java编和Android程中,我们经常使用类似ArrayList,HashMap等这些容器。这些容器少则存储几条,多则上千甚至更多。作为性能调优的一部分,容器调优往往被我们忽略,本文将尝试探索阐述一些关于容器调优中的扩容问题。虽然以Java为例,但是也同样适用于其他编程语言。

        首先以我们最常用的ArrayList为例,它是一个基于数组的List实现。

123456

public static void main(String[] args) {  ArrayList<Object> collection = new ArrayList();  for (int i = 0; i< 1000; i++) {    collection.add(new Object());  }}

        以上代码很简单,不用赘述。那我们使用NetBeans的profile插件 来看一下关于Object对象分配的stacktrace

  

从stacktrace中,我们可以发现

        Object对象trace始于ArrayList.add方法

        经过了一个叫做ArrayList.grow方法

        以上我们可以推断,ArrayList对象发生了扩容操作。因为使用无参的构造方法,会初始化一个存储容量为0的数组。

       如下代码为ArrayList的构造方法

123456789101112131415

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};public ArrayList(int initialCapacity) {  if (initialCapacity > 0) {    this.elementData = new Object[initialCapacity];  } else if (initialCapacity == 0) {    this.elementData = EMPTY_ELEMENTDATA;  } else {    throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);  }}public ArrayList() {  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}

        然而想要容量1000个Object实例,这个过程中则需要不断的扩容,在这个过程中发生了以下几点

        确定新的容量,并以新容量为大小创建新的数组

        将旧数组的数据拷贝到新数组中

        旧的数组将会后续被GC回收掉

        除此之外,扩容还会增加CPU高速缓存的未 命中率。

        因为在JVM中,一般来说,由于对象和其字段常常都需要同时引用,将对象和其字段尽可能放在内存中相邻位置能够减少CPU高速缓存的未命中率。

        而ArrayList扩容后的新数组可能不在于该对象相邻,所以扩容理论上会增加CPU高速缓存的未命中率。

        注意:上面提到的都是CPU高速缓存的未命中率,不是命中率。

更容易扩容的HashMap

        HashMap作为一个高效的key-value的容器,内部也维护了一个Entry数组,也存在扩容的问题。

        然而,HashMap为了更加有效的避免数组冲突,引入了两个概念。

        threshold 阈(yu,四声)值,当内部数据占用量超过这个值,进行扩容。

        loadFactor 通常为0.75,用来计算threshold值,即threshold = 容量 * loadFactor

举个例子

        创建一个HashMap设置初始化容量为16,使用默认的loadFactor 0.75,即threshold为12

        然后不断的填充key,value数据

       当内部数据占用量超过12时,就会触发扩容操作,而不是等到16的时候。

        通常的扩容为双倍扩容,即变成原来的两倍,这里为32.

        因此说HashMap更容易触发扩容,但是这其实是一种在hash与容量占用的一种平衡。

        如何解决或者改善扩容问题

        使用预设较为合理的初始容量

        SQLiteDatabase提供了方便的ContentValues简化了我们处理列名与值的映射,ContentValues内部采用了HashMap来存储Key-Value数据,ContentValues的初始容量是8,如果当添加的数据超过8之前,则会进行双倍扩容操作,

        因此建议对ContentValues填入的内容进行估量,根据实际需要的字段数量,设置合理的初始化数量。

image.png

公司简介

   泉舟时代核心团队为打造互联网、数字城市优质平台汇聚全球行业精英,孵化创建了鹏枫科技(关注数字城市细分行业应用,如:交通、水利环保等),为促进数字文化交流还成立了鑫智会联盟中心(在数字六年经验以上的行业先驱组成的智库)。我们为每一位合作伙伴、更为加入团队的每位人才精英提供更为广阔的施展舞台、职业能力锻炼机遇。

查看更多

联系我们

  •  福州高新区创新园(新师大对面)
  •  北京市庙城镇293号院2号(总部)
  •  上海 黄浦区 龙华东路858号海外滩中心B座
  •  联系电话:0591-8786-1210
  •  手机:18050166663
  •  3058661698@qq.com
  •  http://www.timesqz.com