Java性能权威指南-总结27

news2024/9/21 12:46:15

Java性能权威指南-总结27

  • 数据库性能的最佳实践
    • Java集合类API
      • 同步还是非同步
      • 设定集合的大小
    • 集合与内存使用效率

数据库性能的最佳实践

Java集合类API

Java的集合类API有很大的选择余地;Java 7至少提供了58个不同的集合类。在编写应用时,选择恰当的集合类,以及恰当地使用集合类,是一个重要的性能考量。

使用集合类的第一条规则是,选择适合应用的算法需求的集合类。LinkedList不适合做搜索;如果需要访问一段随机的数据,应该将集合保存到HashMap中。如果数据需要有序排列,则应使用TreeMap,而不是尝试在应用中做排序。如果会用索引访问数据,则使用ArrayList;但如果会频繁地向该数组中间插入数据,则不要使用它,诸如此类。根据算法选择要使用哪个集合类,这非常重要,但是在Java中做选择和在其他编程语言中做选择并没有多少区别。然而在使用Java的集合类时,还有一些特殊的地方需要考虑。

同步还是非同步

默认情况下,几乎所有的Java集合类都是非同步的(主要的例外是Hashtable、Vector及与其相关的类)。

同步的集合类
如果想了解为什么VectorHashtable(及相关类)是同步的,就得先来看一点历史。在Java早期,它们是JDK中仅有的集合类。当时(Java 1.2之前)还没有
集合类框架(Collections Framework)的正式定义;它们只是最初的Java平台提供的几个有用的类。

在Java发布第一个版本时,大部分开发者对多线程知之甚少,而Java试图让开发者能够更容易地避免在多线程环境中编程的某些陷阱。因此,这些类就设计成
了线程安全的。

遗憾的是,在早期的Java版本中,同步——甚至是不存在竞争时的同步——是个很大的性能问题,所以当第一个重大修订版本发布时,集合类框架采用了相反的做
法:所有新的集合类默认都是非同步的。即使从那时开始同步性能已经有了显著提高,但仍然不是没有成本的;能够选择非同步的集合类,可以帮助大家编
写更快的程序(偶尔会出现因并发修改某个非同步的集合而导致的bug)

前面的一个微基准测试,比较了基于CAS的保护和传统的同步。这个例子在多线程的情况下不太实用,但如果问题中的数据只会由一个线程访问,又会怎么样呢?如果不使用任何同步,效果又会如何?下列出了比较情况。因为这里没有试图考虑竞争,所以在这样一种情况下,这里的微基准测试是有效的:没有竞争,手头上的问题是研究同步访问资源有些多余时的损失。

同步访问和非同步访问的性能
在这里插入图片描述

从第2列可以很明显地看出,与简单的非同步访问相比,如果使用了任何一种数据保护技术,都会有比较小的性能损失。然而,这是执行了5亿次操作的一个微基准测试,所以平均到每次操作,差别就只在15纳秒的量级上。如果相关操作在目标应用中执行得足够频繁,性能损失就会有点明显。在大部分情况下,这种差别会被应用中其他更为低效的地方抵消掉。还要记住,这里的绝对数字完全是由测试所运行的目标机器决定的;要获得更为真实的测量结果,测试需要在与目标环境相同的硬件上运行。

那么,如果要在同步的Vector和非同步的ArrayList之间做出选择,该选择哪个呢?访问ArrayList会稍微快一些,这与访问这个列表的频繁程度有关,性能差异是可以测量的。另一方面,这里假设代码不会被多个线程访问。今天可能确实如此,那明天会怎么样呢?如果情况可能会变,那更好的办法是现在就使用同步的集合,并减轻它所带来的性能影响。这是一个设计选择,为使代码经受住时间的考验而将其设计为线程安全的,在这上面投入时间和精力是不是值得,取决于开发应用时的情况。

如果要在非同步集合和使用了CAS法则的集合之间做出选择(比如在HashMapConcurrentHashMap之间),它们的性能差别会微乎其微。当基于CAS的类用于不存在竞争的环境中时,几乎没有什么开销。

设定集合的大小

集合类的用途是保存任意数量的数据元素,并随着集合中新条目的添加,在必要时进行扩展。性能方面有两点需要考虑。

尽管Java中的集合类提供的数据类型非常丰富,但是在基本层面上,这些类都必须仅使用Java基本的数据类型来保存其数据:数值(整型、双精度浮点型等)、对象引用和这些类型的数组。因此,ArrayList中包含一个真正的数组:

private transient Object[] elementData;

随着在ArrayList中添加和移除条目,这些条目会保存在elementData数组内的期望位置(可能会导致数组中的其他条目变更位置)。类似地,HashMap中包含着一个由内部数据类型HashMap$Entry组成的数组,HashMap会将每个键-值对映射到这个数组中,具体位置根据键的哈希码值来确定。

并非所有的集合类都使用数组保存其元素;比如LinkedList,它以内部定义的Node类保存每个数据元素。但是使用数组保存元素的集合类都会涉及一个问题,就是要考虑数组的大小。如何确定某个特定的类是不是属于这个范畴呢?可以看看它的构造函数:如果它有一个构造函数支持指定该集合的初始大小,那它内部就使用了某个数组来存储元素。

对于这样的集合类,精确地指定初始大小非常重要。以ArrayList作为一个简单的例子:elementData数组默认的初始大小为10。当向某个ArrayList实例中插入第11个元素时,它就会扩展elementData数组。这意味着分配一个新数组,将原来的内容复制到这个数组中,然后添加新元素。可以说HashMap使用的数据结构和算法更复杂一些,但基本原理是一样的:在某一时刻,必须重新调整内部数据结构的大小。

ArrayList类调整数组大小的方法是,在现有基础上增加约一半。所以elementData数组的大小最初是10,然后是15,22,33,以此类推。不管使用何种算法调整数组大小(参见后面方框内的文字),都会导致一些内存被浪费(这反过来又会影响应用花在执行GC上的时间)。此外,每当数组必须调整大小时,都伴随一个成本很高的数组复制操作,将老数组中的内容转移到新数组中。

要减少这些性能损失,必须尽可能准确地估计一下集合最终的大小,并用这个值来构建集合。

非集合类中的数据扩展
很多非集合类也会在内部数组中保存大量数据。比如,ByteArrayOutputStream类必须把写入到该流中的所有数据保存到一个内部缓冲区中;类似地,
StringBuilderStringBuffer类也必须将所有字符保存到一个内部的字符数组中。

这些类大多会使用同样的算法调整内部数组的大小:需要调整时就加倍。这意味着,平均而言,内部的数组要比当前包含的数据多25%。

这里的性能考量是相似的:使用的内存量多于ArrayList这个例子,需要复制数据的次数要少一些,但原理是一样的。在构建某个对象时,
如果可以设置其大小,可以评估一下这个对象最终会保存多少数据,然后选择接受大小参数的那个构造函数。

集合与内存使用效率

集合的内存使用效率没有达到最佳的例子:在用于保存集合中的元素的底层存储中,往往会浪费一些内存。

对于元素比较稀疏的集合(只有一两个元素),这存在较大的问题。如果这样的集合用得非常多,则会浪费大量内存。解决方案之一就是在创建集合时设定其大小。另一种方案是,考虑一下这种情况是不是真的需要集合。

大部分开发者被问及如何快速地排序任意一个数组时,答案都会是快速排序(quicksort)。而好的性能工程师希望了解数组的大小:如果数组足够小,那最快的方式是使用插入排序(insertion sort)。(对于较小的数组来说,基于快速排序的算法通常会使用插入排序;就Java而言,Arrays.sort()方法的实现就假定,少于47个元素的数组用插入排序比用快速排序更快。)数组大小至关重要。

JDK 7u40中集合类内存大小

很多应用中经常出现因集合类使用不当而导致的问题,所以JDK 7u40向ArrayListHashMap的实现中引入了一个新的优化:默认情况下(比如在调用构造函数
时没有使用大小参数),这些类不再为数据分配任何底层存储,而是在向该集合中插入第一个元素时才分配。

这就是延迟初始化技术的一个例子,在测试一些常见的应用时,因为减少了对GC的需求,所以性能有所改进。这些应用中有很多从来没用过的集合;
所以延迟分配其底层存储在性能方面有优势。因为每次访问时本来就要检查底层存储的大小,所以检查底层存储是不是已经分配,并没有性能损失
(不过创建最初的底层存储所需要的时间从创建对象时变成了向对象中插入第一个数据时)

类似地,在基于某个键值查找数据时,HashMap是最快的;但如果只有一个键,与使用一个简单的对象引用相比,使用HashMap就是大材小用了。即使有几个键,维护几个对象引用所需要的内存也比一个完整的HashMap对象少,而且这样对GC也有积极的影响。

除了以上这些,关于集合类的内存使用还有很重要的一点区别需要了解,那就是HashMap对象和ConcurrentHashMap对象大小的差别。Java 7之前,一个空的或者元素稀疏的ConcurrentHashMap对象非常大:超过1KB(即便向其构造函数传了一个很小的大小)。Java7中,其大小只有208字节(与之相比,构造时没有指定大小的空HashMap占128字节,指定大小为1的HashMap占72字节)。

在存在很多小型Map的应用中,大小的差别仍然非常重要,但是Java 7中引入的优化使得这种差别不那么显著了。为提高性能,在内存非常重要且存在大量Map的应用中,有人建议避免使用ConcurrentHashMap类。这些建议的核心其实是两个因素之间的取舍:是要更快地访问Map(如果存在竞争),还是要小心更大的Map所引发的对垃圾收集器的压力。这个取舍如今仍然存在,但重心已更多地偏向了使用ConcurrentHashMap

快速小结

  1. 仔细考虑如何访问集合,并为其选择恰当的同步类型。不过,在不存在竞争的条件下访问使用了内存保护的集合(特别是使用了基于CAS的保护的集合),性能损失会极小;有时候,保证安全性才是上策。
  2. 设定集合的大小对性能影响很大:集合太大,会使得垃圾收集器变慢;集合太小,又会导致大量的大小调整与复制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/736358.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构算法题——数组

leetcode-1.两数之和 leetcode-1.两数之和 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在…

阿里云AliYun物联网平台使用-申请免费试用及完成初始配置

一、项目简介 本专栏文章将围绕阿里云物联网平台,实现其设备向云平台的数据上传,客户端获取云平台数据。设备通过NBIOT技术实现无线采集,定时上传。 二、阿里云平台申请 阿里云物联网平台试用申请地址 进入上述超链接网址: 由于是…

【InnoDB 存储引擎】15.7.1 InnoDB Locking(锁实验,包含了如 记录锁、间隙锁、Next-Key Lock 算法等,重要)

文章目录 1 关于 Record Lock 的实验1.1 实验 1:没有主键时的如何锁定1.2 实验 1(续):带着问题继续实验1.3 实验 2:有主键时如何锁定 2 关于 Next-Key Lock 的实验2.1 实验 3:如何确定算法的锁定范围2.2 实…

VS 字体不对齐解决方案

1. 问题描述 输入相同数量但不是同一类型的字符的字符,会出现字符显示不对齐的问题。 在某些需要根据对齐来写的代码的时候,这种情况是相当的折磨。 2. 解决方案 设置等宽字体。 依次点击 VS 上方的 工具 → 选项 → 字体和颜色 → 字体 → 随便选择一款…

基于simulink识别彩色视频序列中的交通警告标志

一、前言 此示例演示如何识别彩色视频序列中的交通警告标志,如“停止”、“请勿进入”和“让行”。 二、模型 下图显示了交通警告标志识别模型: 三、交通警告标志模板 该示例使用两组模板 - 一组用于检测,另一组用于识别。 为了节省计算…

Linux常用命令——ex命令

在线Linux命令查询工具 ex 启动vim编辑器的ex编辑模式 补充说明 在ex模式下启动vim文本编辑器。ex执行效果如同vi -E,适用于法及参数可参照vi指令,如要从Ex模式回到普通模式,则在vim中输入:vi或:visual即可。 语法 ex(参数&…

【算法设计与分析】集合相等问题——设计一个拉斯维加斯算法,对于给定的集合S和T,判定其是否相等。

目录 一、问题描述二、问题分析三、运行结果四、源代码 一、问题描述 给定两个集合S和T,试设计一个判定S和T是否相等的蒙特卡洛算法。 数据输入: 由文件input.txt给出输入数据。第1行有1个正整数n,表示集合的大小。接下来的2行,每…

路径规划算法:基于梯度优化的路径规划算法- 附代码

路径规划算法:基于梯度优化的路径规划算法- 附代码 文章目录 路径规划算法:基于梯度优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能优化算法梯度…

关于线程池其他一些知识点的讨论

文章目录 前言一、线程池的几种任务队列1.ArrayBlockingQueue2.LinkedBlockingQueue3.DelayedWorkQueue4.SynchronousQueue 二、如何确定核心线程数?三、线程池的种类有哪些?1.newFixedThreadPool2.newSingleThreadExecutor3.newCacheThreadPool4.newSch…

AI掌绘艺术:揭秘Stable Diffusion华美图韵背后那些提示词的秘密

开篇 好了好了,我知道这个标题有点大,大得像我妈的锅一样。但是,我保证,当你读完这篇文章后,你不仅会明白我为什么敢用这样的标题,而且你也会想试试宝贵的AI画画方法。 首先,我要说&#xff0…

【ARM Coresight 系列文章 3.1 - ARM Coresight DP 对 AP 的访问 1】

文章目录 1.1 DP 中相关寄存器的介绍1.1.1 DPACC and APACC 寄存器1.1.2 DP SELECT 寄存器1.1.3 AP CSW寄存器1.1.4 AP TAR 寄存器1.1.5 AP DRW寄存器1.1.6 AP Banked Data registers 1.1 DP 中相关寄存器的介绍 如果DAP接入的是JTAG接口,那么将会通过APACC寄存器来…

idea中非常好用的配置

1、自动导包和优化多余的包 2、显示行号、方法之间的分隔符 3、忽略大小写进行提示 4、多个类不隐藏,多行显示 5、鼠标悬浮代码提示 6、加入类之前的稳定注释模板 好了,文章就分享到这里了,看完之后如果对你有所帮助,不要忘了点赞…

【机器学习核心总结】什么是长短时记忆网络(LSTM)

什么是长短时记忆网络(LSTM) RNN有一定的记忆能力,但不幸的是它只能保留短期记忆,在各类任务上表现并不好,那该怎么办? 人们将目光投向了自己,人类的记忆是有取舍的,我们不会记住每时每刻发生的所有事,会…

学无止境·MySQL④(多表查询)

多表查询试题 试题一1、创建表2、表中添加数据3、查询每个部门的所属员工4、查询研发部门的所属员工5、查询研发部和销售部的所属员工6、查询每个部门的员工数,并升序排序7、查询人数大于等于3的部门,并按照人数降序排序 试题一 1、创建表 use mydb3; – 创建部门…

技术文本的写作问题

最近刚参与一位大佬的重要文本撰写,也就3000字,磨了有几个月。 收获很多。关于技术写作,要写透,要写简单;要舍得放弃。 写透看起来简单,其实是最难的。一般人看到有字数限制就拼命压缩文本,把…

从零开始制作一个Web蜜罐扫描器(1)

从零开始制作一个Web蜜罐扫描器(0)_luozhonghua2000的博客-CSDN博客 蜜罐有什么特征 根据我们上面的描述,现在可以理解,蜜罐的功能是诱捕黑客. 诱捕黑客是利用的jsonp劫持技术来做。 jsonp劫持技术需要利用jsonp跨域请求很多其他的社交网站页面从而获得攻击者的相关信息。 因…

SpringBoot第20讲:SpringBoot如何对接口进行签名

SpringBoot第20讲:SpringBoot如何对接口进行签名 在以SpringBoot开发后台API接口时,会存在哪些接口不安全的因素呢?通常如何去解决的呢?本文是SpringBoot第20讲,主要介绍API接口有不安全的因素以及常见的保证接口安全的…

青岛大学_王卓老师【数据结构与算法】Week04_12_案例分析与实现2_学习笔记

本文是个人学习笔记,素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享,另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权,请留言作删文处理。 课程视频链接: 数据结构与算法基础–…

Python+OpenCV人行道盲道边缘侦测识别

程序示例精选 PythonOpenCV人行道盲道边缘侦测识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonOpenCV人行道盲道边缘侦测识别>>编写代码&#xff0c;代码整洁&#x…

使用 Jest 在 Visual Studio Code 中进行更好的单元测试

目录 前言&#xff1a; 使用 Jest 扩展显著改善测试流程 1.自动启动 Jest 测试 2. 显示单个失败/通过的测试用例 允许调试单元测试 在文件中显示代码覆盖率 结论 前言&#xff1a; Jest是一个流行的JavaScript测试框架&#xff0c;它提供了简洁、灵活和强大的工具来编写…