Java性能权威指南-总结15

news2024/11/28 8:28:53

Java性能权威指南-总结15

  • 堆内存最佳实践
    • 对象生命周期管理
      • 弱引用、软引用与其他引用
    • 小结

堆内存最佳实践

对象生命周期管理

弱引用、软引用与其他引用

在Java中,弱引用和软引用也支持对象重用,不过作为开发者,并不会经常从重用的角度看待它们,并一般性地将其称作非确定引用。这些类引用更多用于缓存一个较长的计算或者一个数据库查询的结果,而非用于重用对象。比如,在股票Servlet中,可以用一个非确定引用来缓存getHistory()方法(该方法需要很长的计算,或者需要很长的数据库调用)的结果。这个结果只是一个对象,当通过非确定引用来缓存它时,只是简单地重用了该对象,不然的话,初始化开销会很高。

术语说明

引用(Reference)
引用(或者说对象引用)可以是任何类型的引用:强引用、弱引用、软引用等。指向一个对象的普通引用实例变量就是一个强引用。

非确定引用(Indefinite reference)
使用这个术语来区分强引用和其他特殊引用(比如软引用或弱引用)。一个非确定引用其实是一个对象实例(比如,SoftReference类的一个实例)。

所引对象(Referent)
非确定引用的工作方式是,在非确定引用类的实例内,嵌入另一个引用(几乎总是嵌入一个强引用)。被封装的对象称作“所引对象”。

该术语也反映出这样一点:没有人说“缓存”一个线程用于重用,但是将在缓存数据库操作结果方面探索非确定引用的重用。

与对象池或线程局部变量相比,非确定引用的优势在于,它们最终会被垃圾收集器回收。 如果对象池中包含了已经执行的最后10000个股票查询,堆的运行就会变慢,应用也会受牵连:去掉那10000个元素所占据的堆,剩下的就是应用可以使用的其余堆了。如果这些查询是通过非确定引用保存的,JVM就可以释放一些空间(取决于引用的类型),从而获得更好的GC吞吐量。

非确定引用的缺点是对垃圾收集器的效率会有轻微影响。图一对比了不使用与使用非确定引用时内存的使用情况(这里用的是弱引用)。
图一
被缓存的对象占了512字节。在左侧的就是消耗的所有内存(没有指向对象的实例变量占据的内存)。在右侧,对象被缓存在一个SoftReference内,额外增加了40字节的内存消耗。**非确定引用和其他任何对象一样:它们也消耗内存,而且其他变量(图中右侧的cachedValue变量)也是通过强引用引用它们。**所以对垃圾收集器的第一个影响是,非确定引用会导致应用使用更多内存。对垃圾收集器的更大的影响体现为,垃圾收集器要回收非确定引用,至少需要两个GC周期。

下图说明了当一个所引对象不再被强引用时(即lastViewed被设置为null),会发生什么。如果没有对StockHistory对象的引用,在下一次GC期间,该对象会被释放。所以图二的左侧现在消耗的内存为0字节。
在这里插入图片描述
在图的右侧,仍然有内存消耗。所引对象被释放的精确时机,会随非确定引用类型的不同而有所不同,暂时只考虑软引用的情况。所引对象将仍然逗留在内存中,直到JVM确定近期不会再使用它。当这个条件出现时,第一次GC会释放所引对象,但不是非确定引用本身。应用最终的内存状态如图三所示。
在这里插入图片描述
现在,对于非确定引用对象本身,(至少)有两个强引用指向它:由应用创建的原始的强引用,再就是由JVM创建的、在所引对象队列上的一个新的强引用。在非确定引用对象本身被垃圾收集器回收之前,必须先清理掉所有这些强引用。

这种代码通常是由处理引用队列的代码处理的。如果在队列上有新对象创建,代码会得到通知,并立即移除指向该对象的所有强引用。之后,在下一个GC期间,非确定引用对象会被释放。最糟糕的情况是,引用队列没有立即被处理,有可能要经过多个GC周期,才能将一切清理干净。然而即便在最好的情况下,非确定引用在释放之前也必须经历两个GC周期。

依赖于非确定引用的类型,处理算法也有较大差异,但是所有的非确定引用某种程度上都有这类性能损失。

  1. 软引用

如果问题中的对象以后有很大的机会重用,可以使用软引用,但是如果该对象近期一直没有使用到(计算时也会考虑堆还有多少内存可用),垃圾收集器会回收它。软引用本质上是一个比较大的、最近最久未用(LRU)的对象池。获得较好性能的关键是确保它们会被及时清理。

来看一个例子。股票Servlet可以设置一个股票历史的全局缓存,以股票代码(或者代码与日期)为键。比如有请求要获取TPKS从2013年6月1日到2013年8月31日之间的股价历史,可以先看看缓存,其中是不是有以前类似请求的结果。

之所以要缓存数据,原因是对某类数据的请求往往会比其他数据更多。 如果对TPKS这支股票的请求最多,就可以考虑将其保存到软引用缓存中。另一方面,查询一次KENG这支股票,其结果也会在缓存中停留一段时间,但最终会被回收。对于随时间变化的请求,也是如此:对DNLD的一群请求,可以利用第一次请求的结果。如果用户意识到DNLD是笔糟糕的投资,那些缓存的条目最终会从堆中去掉。

精确地讲,一个软引用何时会被释放呢?首先,所引对象一定不能有其他的强引用。如果软引用是指向其所引对象的唯一引用,而且该软引用最近没有被访问过,则所引对象会在下一次GC周期释放。 具体而言,其关系可以用如下伪代码表示:

long ns = SoftRefLRUPolicyMSPerMB * AnountofFreeMemoryInMB;
if (now - last_access_to_reference > ms)
	free the reference

这里有两个关键值。第一个是由-XX:SoftRefLRUPolicyMSPerMB=N标志设置的,默认值为1000。

第二个是堆中空闲内存的数量(在一个GC周期完成之后)。因为堆的大小是动态变化的,在计算堆中有多少内存可用时,JVM有两个选择:堆中目前的空闲内存数量,或者堆扩展到最大容量后的空闲内存数量。这些值的选择是由所用的编译器确定的。client编译器是基于当前堆中的可用值,而server编译器堆的最大可能值。

那这都是怎么工作的呢?以使用server编译器、堆空间为4GB的JVM为例,在一次Full GC(或一个并发周期)之后,堆可能被占用了50%,因此空闲堆是2GB。SoftRefLRUPolicyMSPerMB的默认值(1000)意味着在过去的2048秒(2048000毫秒)内没有访问到的任何软引用都会被清理:空闲堆是2048(MB),再乘以1000:

long ms = 2048000;// 1000 * 2048
if (System.currentTimeMillis() - last_access_to_reference_in_ms > ms)
	free the reference

**如果4GB的堆占用了75%,则过去的1024秒内没有访问到的对象会被回收,以此类推。**要更频繁地回收软引用,可以降低SoftReflRUPolicyMSPerNB标志的值。将该值设置为500,意味着堆大小为4GB的JVM如果占用了75%,则会回收过去512秒没有访问到的对象。

如果堆很快就会被软引用填满,则调优该标志往往是必要的。假设堆有2GB空闲,应用开始创建软引用。如果它在不到2048秒(大概是34分钟)创建了1.7GB的软引用,则这些软引用都不满足回收条件。这样,堆中留给其他对象的空间就只有300 MB了;这会导致GC频繁进行(对整体性能影响很坏)。

如果JVM完全耗尽了内存,则会出现非常严重的颠簸(thrashing),它会清理掉所有的软引用,否则会抛出OutofMemoryError 不抛出错误当然好,但是不分青红皂白地丢掉所有缓存的结果,可能也不理想。因此,另一个降低SoftRefLRUPolicyMSPerMB的值的时机是,当引用处理日志说明有大量软引用意外被清理时。另一方面,对于长期运行的应用,如果满足如下两个条件,可以考虑增大SoftRefLRUPolicyMSPerMB的值:

  • 有很多空闲堆可用;
  • 软引用会频繁访问。

这种情况非常罕见。这与设置GC策略所讨论的情况类似:可以想象,如果增加了软引用策略的值,就是告诉JVM,不到万不得已不要释放软引用。确实如此,但是这同时也告诉JVM,堆中不要给正常操作留任何空间,结果很可能会导致把很多时间花在GC上。
应该注意的是,不要使用太多软引用,因为它们很容易填满整个堆。与提防创建包含太多实例的对象池相比,这一点更应该注意:如果对象的数目不是特别大,软引用就会工作得很好。否则,就要考虑用更传统的、固定大小的对象池来实现一个LRU缓存。

  1. 弱引用

**当问题中的所引对象会同时被几个线程使用时,应该考虑弱引用。否则,弱引用很可能会被垃圾收集器回收:只有弱引用的对象在每个GC周期都可以回收。**这意味着,弱引用绝对不会进入图二所示的软引用的状态。当强引用被移除时,弱引用会立即释放。因此程序的状态直接从图一到达图三。

这里有个有趣的现象,弱引用会在堆中终结。引用对象就和其他Java对象一样:在年轻代中创建,最终会被提升到老年代。如果弱引用本身仍然在年轻代中,而弱引用的所引对象被释放了,则弱引用可以快速释放(下一次Minor GC时)。如果弱引用的所引对象存在了足够长的时间,被提升到了老年代中,则弱引用在下一次并发或Full GC周期内才会释放。

以股票Servlet的缓存为例,假设知道某个特定用户会在其会话期间访问TPKS,他几乎总会再次访问。在该用户的HTTP会话中,用一个强引用来保存股票的值是有意义的:它会一直存在,一旦用户登出,HTTP会话就会被清理,而内存也会被回收。

如果另一个用户来了,而且也需要TPKS的数据,那如何找到数据呢?因为对象在内存中的某个地方,不希望程序重新去查找,但是Servlet代码不能搜索其他用户的会话数据,因此,除了在第一个用户的HTTP会话中保存一个指向TPKS数据的强引用外,在一个全局缓存中保存一个弱引用,指向那个数据,也是有意义的。现在第二个用户就能查找TPKS数据了,当然这是以第一个用户没有登出并清理会话为前提的。

这就是所谓的同时访问。这就好比是告诉JVM:“嘿,只要有其他人对这个对象感兴趣,就让我知道它在哪儿,但是如果他们不再需要它了,就把它丢弃,我自己会重新创建。”比较弱引用与软引用,软引用基本像是在说:“嘿,只要有足够的内存,而且看上去有人会偶尔访问它,那就保留着它。”

如果不理解这种区别,在使用弱引用时就经常会出现性能问题。不要认为除了释放更快,弱引用和软引用就是一样的,别犯这种错误:软引用的对象通常可以存活几分钟甚至几小时,但是只要所引对象仍然存在,弱引用对象就一直存活。(下一个GC周期会清理。)

快速小结

  1. 非确定引用(包括软引用、弱引用、虚引用和最终引用)会改变Java对象正常的生命周期,与池或线程局部变量相比,它可以以对GC更为友好的方式实现对象重用。
  2. 当应用对某个对象感兴趣,而且该对象在应用中的其他地方有强引用时,才应该使用弱引用。
  3. 软引用保存可能长期存在的对象,提供了一个简单的、对GC友好的LRU缓存。
  4. 非确定引用自身会消耗内存,而且会长时间抓住其他对象的内存;应该谨慎使用。
    

小结

内存管理对Java程序的快慢至关重要。调优GC非常重要,但是要获得最好的性能,在应用内必须有效地利用内存。

目前的硬件趋势往往不鼓励开发者考虑内存:编程中通常的时间和空间之间的取舍,有可能会变成时间和空间与时间(time/space-and-time)之间的取舍:使用太多堆空间可能会降低性能,因为需要更多GC。在Java中,管理堆仍然非常重要。

多数管理问题都围绕何时以及如何使用特殊的内存技术展开:对象池、线程局部变量和非确定引用。明智地使用这些技术可以极大改进应用性能,但是过度使用也很容易引起性能下降。在限量的情况下,也就是问题中的对象数目很少,而且有个边界时,使用这些内存技术会非常高效。

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

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

相关文章

【C语言进阶】文件操作

目录 🥅什么是文件: 🏑程序文件:🏑数据文件: 🏑文件名: 🥅文件的打开和关闭:🏑文件指针: 🏑fopen和fclose: &a…

基于matlab使用先导校准来补偿阵列不确定性(附源码)

一、前言 此示例说明如何使用先导校准来提高天线阵列在存在未知扰动时的性能。 原则上,可以轻松设计理想的均匀线性阵列(ULA)来执行阵列处理任务,例如波束成形或到达方向估计。在实践中,没有理想的阵列。例如&#xff…

io.netty学习(二)Netty 架构设计

目录 前言 Selector 模型 SelectableChannel Channel 注册到 Selector SelectionKey 遍历 SelectionKey 事件驱动 Channel 回调 Future 事件及处理器 责任链模式 责任链模式的优缺点 ChannelPipeline 将事件传递给下一个处理器 总结 前言 上一篇文章&#xff…

1.react路由的基本使用

第一步 首先打开index.js,在里面引入BrowserRouter或者HashRouter,启用全局路由模式。 BrowserRouter与HashRouter的区别 // index.js import React from react; import ReactDOM from react-dom/client; import ./index.css; import App from ./App;…

P31[10-1]软件模拟IIC通信协议(使用stm32库函数)(内含:实物连接+IIC时序解释+硬件电路+IIC基本时序单元(起始 终止 发送接收 ))

IIC通讯分为硬件读写IIC和软件IIC,以下为软件读写IIC 实物连接如下: 解释: 软件IIC通讯,对MPU6050芯片内部的寄存器进行读写操作,。写入配置寄存器,即可对外挂模块进行配置。。读出数据寄存器,即可获取外挂模块的数据。。。 OLED第一行为设备ID号(固定,有些可能不同)…

基于Springboot+mybatis+mysql+html图书管理系统2

基于Springbootmybatismysqlhtml图书管理系统2 一、系统介绍二、功能展示1.用户登陆2.用户主页3.图书查询4.还书5.个人信息修改6.图书管理(管理员)7.学生管理(管理员)8.废除记录(管理员) 三、数据库四、其它…

8.compute部署neutron服务

compute节点 安装软件包 yum -y install openstack-neutron-linuxbridge ebtables ipset 修改配置文件:/etc/neutron/neutron.conf #查看文件属性 ll /etc/neutron/neutron.conf -rw-r----- 1 root neutron ... /etc/neutron/neutron.conf #备份配置文件 cp /e…

C语言:将一句话的单词进行倒置,标点不倒置。

题目: 将一句话的单词进行倒置,标点不倒置。(字符数组长度不超过100) 比如:I like beijing. 经过函数后变为:beijing. like I 思路: 总体思路: (可以把两步顺序调换&am…

chatgpt赋能python:Python中寻找不重复字符的方法

Python中寻找不重复字符的方法 Python是一种著名界面友好、易学易用且功能强大的编程语言,广泛应用于各种需求中。在本篇文章中,我们将会讨论如何使用Python编程语言来寻找一个字符串中的不重复字符。我们将简单介绍如何实现这个过程以及为什么这个过程…

Python小白如何利用GPT4快速开发一个网站!

这个是一个全栈的项目,麻雀虽小,五脏俱全!全程都是利用gpt4进行辅助编程搞定的。第一版其实非常快,大概30分钟就搞定了,后续就是不断的添砖加瓦,增加功能和优化UI。 其实很多小白都在说要学Python&#xff…

chatgpt赋能python:Python扩展库需要导入吗?重要性与结论解析

Python扩展库需要导入吗?重要性与结论解析 作为业内最受欢迎的编程语言之一,Python在数据科学、人工智能、Web开发等领域拥有大量的应用。在Python开发过程中,扩展库的使用是不可避免的。本文将介绍Python扩展库的重要性以及是否需要导入的问…

【Arduino】Portenta H7 板子介绍

文章目录 1. Features2. Pins Name3. Functions3.1 analogReadResolution()3.2 millis() Ref. 1. Features 2. Pins Name 3. Functions 3.1 analogReadResolution() analogReadResolution() is an extension of the Analog API for the Zero, Due, MKR family, Nano 33 (BLE …

vue3-实战-10-管理后台-权限管理之用户管理模块开发

目录 1-用户首页列表开发 1.1-需求原型分析 1.2-封装请求和数据类型 1.3-数据页面渲染和展示 1.4-点击搜索按钮搜索用户 1.5-点击重置按钮 2-新增编辑用户 2.1-原型需求分析 2.2-表单页面数据收集 2.3-页面校验规则的定义 2.4-添加用户按钮编辑按钮逻辑 2.5-保存和取…

C++个人通信录系统

背景: 使用C编写一个通信录程序,来完成作业上的一些需求。 1-提供录入个人信息、修改个人信息(姓名和出生日期除外)、删除个人信息等编辑功能 2-提供按姓名查询个人信息的功能 3-提供查找在5天之内过生日的人员的信息&#xf…

Gof23设计模式之工厂方法模式和抽象工厂模式

在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。 如果我们…

机器学习之K-Means(k均值)算法

1 K-Means介绍 K-Means算法又称K均值算法,属于聚类(clustering)算法的一种,是应用最广泛的聚类算法之一。所谓聚类,即根据相似性原则,将具有较高相似度的数据对象划分至同一类簇,将具有较高相异…

NDK使用LLVM编译Boost库给Android使用

1.下载boost库 ​ wget https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.gz​ 选择1.71.0版本 NDK版本19 ,ANDROID版本 24 进入然后后的目录 (不指定平台 默认为当前系统平台) ./bootstrap.sh --prefix=./android_build --libdir=.…

Java---阶段项目----五子棋

Java---阶段项目----五子棋 需求说明技术实现棋盘制作完整代码 需求说明 五子棋棋盘为一个1010的方格,五子棋玩家共为两个(A,B),A在棋盘上落子后,B再落子,依次往复,直到一方胜利或者棋盘空间用完为止,判断…

StarCCM+ 命令行运行(Windows)

添加环境变量 找到启动程序的位置。找到当初安装starccm的文件夹,一般就是 Siemens 文件夹,进入会看到各版本的安装文件夹(如果你没有安装多个版本则只有一个),然后参考下面我的路径找到相应的文件夹。在bin文件夹内可…

【openGauss简单使用---快速入门】

【openGauss简单使用---快速入门】 🔻 一、openGauss使用🔰 1.1 连接openGauss🔰 1.2 创建数据库用户和授权🔰 1.3 创建数据库🔰 1.4 创建SCHEMA🔰 1.5 创建表 🔻 二、总结—温故知新 &#x1f…