Java应用程序中的内存泄漏和内存管理

Java平台最突出的功能之一是其自动内存管理。 许多人错误地将此功能转换为Java中没有内存泄漏 。 但是,事实并非如此,我给人的印象是,现代Java框架和基于Java的平台,尤其是Android平台,越来越与这种错误的假设相矛盾。 为了对Java平台上的内存泄漏如何产生印象,请查看以下堆栈实现:

class SimpleStack {private final Object[] objectPool = new Object[10];private int pointer = -1;public Object pop() {if(pointer < 0) {throw new IllegalStateException("no elements on stack");}return objectPool[pointer--];}public Object peek() {if(pointer < 0) {throw new IllegalStateException("no elements on stack");}return objectPool[pointer];}public void push(Object object) {if(pointer > 8) {throw new IllegalStateException("stack overflow");}objectPool[++pointer] = object;}
}

此堆栈实现以数组形式存储其内容,并另外管理一个指向当前活动堆栈单元的整数。 每当元素从堆栈顶部弹出时,此实现都会导致内存泄漏。 更准确地说,堆栈将保留对数组顶部元素的引用,即使不再使用它也是如此。 (除非再次将其压入堆栈,否则将导致引用被完全相同的引用覆盖。)因此,即使在释放了对该对象的所有其他引用之后,Java也将无法对其进行垃圾回收。 由于堆栈实现不允许直接访问基础对象池,因此,在将新元素推入堆栈的相同索引之前,此不可访问的引用将阻止对引用对象进行垃圾回收。

幸运的是,这种内存泄漏很容易解决:

public Object pop() {if(pointer < 1) {throw new IllegalStateException("no elements on stack");}try {return objectPool[pointer];} finally {objectPool[pointer--] = null;}}

当然,在日常Java开发中,内存结构的实现并不是一项非常常见的任务。 因此,让我们看一个更常见的Java内存泄漏示例。 这种泄漏通常是由常用的观察者模式引起的 :

class Observed {public interface Observer {void update();}private Collection<Observer> observers = new HashSet<Observer>();void addListener(Observer observer) {observers.add(observer);}void removeListener(Observer observer) {observers.remove(observer);}}

这次,存在一种允许直接从基础对象池中删除引用的方法。 只要任何已注册的观察者在使用后都从外部取消注册,就不会在此实现中担心任何内存泄漏。 但是,请设想一个场景,在这种情况下,您或框架的用户在使用观察器后会忘记注销注册。 同样,观察者将永远不会被垃圾回收,因为观察者会一直引用它。 更糟糕的是,如果没有对这个现在无用的观察者的引用,就不可能从外部从观察者的对象池中删除观察者。

但是,这种潜在的内存泄漏也很容易解决,其中涉及使用弱引用 ,这是我个人希望程序员会更加意识到的Java平台功能。 简而言之,弱引用的行为类似于普通引用,但不会阻止垃圾回收。 因此,如果没有剩余的强引用,并且JVM执行了垃圾回收,则可以突然发现弱引用为null。 使用弱引用,我们可以像这样更改上面的代码:

private Collection<Observer> observers = Collections.newSetFromMap(new WeakHashMap<Observer, Boolean>());

WeakHashMap是地图的现成实现,使用弱引用包装其键。 通过此更改,被观察者将不会阻止其观察者进行垃圾收集。 但是,您应该始终在Java文档中指出此行为! 如果您的代码用户想要像日志实用程序一样向您的观察者注册永久观察者,而他们不打算对其进行引用,则可能会造成很大的混乱。 例如,Android的OnSharedPreferencesChangeListener使用弱引用来监听,而没有记录此功能。 这可以让您彻夜难眠!

在本博客文章的开头,我建议当今的许多框架都需要其用户进行仔细的内存管理,并且我想就该主题至少给出两个示例来解释这一问题。

Android平台:

Android编程为核心应用程序类引入了生命周期编程模型。 总而言之,这意味着您无法控制自己创建和管理这些类的对象实例,而是可以在需要时由Android OS为您创建它们。 (例如,如果您的应用程序应该显示特定的屏幕。)以同样的方式,Android将决定何时不再需要特定的实例(例如,当用户关闭应用程序的屏幕时),并通知您有关信息。通过在实例上调用特定的生命周期方法进行删除。 但是,如果让对该对象的引用进入某些全局上下文,则Android JVM将无法按照其意图进行垃圾回收。 由于Android手机通常在内存方面受到限制,并且因为Android的对象创建和销毁例程甚至对于简单的应用程序都可能变得非常疯狂,因此您必须格外小心以清理引用。

不幸的是,对核心应用程序类的引用很容易消失。 在下面的示例中,您可以发现滑动参考吗?

class ExampleActivity extends Activity {@Overridepublic void onCreate(Bundle bundle) {startService(new Intent(this, ExampleService.class).putExtra("mykey",new Serializable() {public String getInfo() {return "myinfo";}}));}
}

如果您认为这是intent的构造函数中的this引用,那您是错误的。 该意图仅用作服务的启动命令,并且在服务启动后将被删除。 取而代之的是,匿名内部类将保留对其封闭类的引用,即ExampleActivity类。 如果接收的ExampleService保留对该匿名类的实例的引用,则结果还将保留对ExampleActivity实例的引用。 因此,我只能建议Android开发人员避免使用匿名类。

Web应用程序框架(特别是

Web应用程序框架通常在会话中存储半永久性用户数据。 无论您写入会话的什么内容,通常都会在内存中保留不确定的时间。 如果您在有大量访问者的情况下浪费了会话,则servlet容器的JVM迟早会打包。 Wicket框架是需要格外小心的一个极端示例:Wicket序列化用户以版本化状态访问的任何页面。 简单地说,这意味着如果网站的访问者之一单击您的欢迎页面十次,Wicket将以其默认配置在您的硬盘驱动器上存储十个序列化对象。 这需要格外小心,因为Wicket页面对象持有的所有引用都将导致这些引用对象与页面一起被序列化。 看一下这个不好的实践Wicket示例:

class ExampleWelcomePage extends WebPage {private final List<People> peopleList;public ExampleWelcomePage (PageParameters pageParameters) {peopleList = new Service().getWorldPhonebook();}
}

通过十次单击欢迎页面,您的用户仅将十本世界电话簿副本存储在服务器硬盘驱动器上。 因此,请始终在Wicket应用程序中使用LoadableDetachableModel ,它将为您提供参考管理。

跟踪Java应用程序中的内存泄漏可能很麻烦,因此,我想将JProfiler命名为有用的(但不幸的是非免费的)调试工具。 它允许您以堆转储的形式浏览Java正在运行的应用程序的内部。 如果内存泄漏对于您的应用程序来说是一个问题,我建议您尝试一下JProfiler。 有可用的评估许可证。

进一步的阅读 :如果要在自定义类加载器时看到另一个有趣的内存泄漏事件,请参阅Zeroturnaround博客 。

参考: My Daily Java博客上的JCG合作伙伴 Rafael Winterhalter提供了Java应用程序中的内存泄漏和内存管理 。

翻译自: https://www.javacodegeeks.com/2014/01/memory-leaks-and-memory-management-in-java-applications.html

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

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

相关文章

js (jQuery)分组数据

function getobjArr (data) {var result [];data.HELMET.system 系统分类// console.log(data)$.each(data.HELMET, function (index_h, elem_h) {var h {id: index_h,name: elem_h,Projects: []}$(data.sonProjects).each(function (index_p, elem_p) {elem_p.AppCategory…

【前端组件】

下拉列表&#xff1a;https://harvesthq.github.io/chosen/#optgroup-support转载于:https://www.cnblogs.com/helww/p/9718396.html

python后台开发知识点_面试总结:鹅厂Linux后台开发面试笔试C++知识点参考笔记...

文章每周持续更新&#xff0c;各位的「三连」是对我最大的肯定。可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇)文章是由自己笔试面试腾讯的笔记整理而来&#xff0c;整理的时候又回顾了一遍&#xff0c;中间工作忙断断续续整理了半个月&#xf…

python用turtle画彩虹_Python利用turtle库绘制彩虹代码示例

语言&#xff1a;PythonIDE&#xff1a;Python.IDE需求做出彩虹效果颜色空间RGB模型&#xff1a;光的三原色&#xff0c;共同决定色相HSB/HSV模型&#xff1a;H色彩&#xff0c;S深浅&#xff0c;B饱和度&#xff0c;H决定色相需要将HSB模型转换为RGB模型代码示例&#xff1a;#…

MongoDB事实:商品硬件上每秒插入80000次以上

在尝试一些时间序列集合时&#xff0c;我需要一个大数据集来检查我们的聚合查询在增加数据负载的情况下不会成为瓶颈。 我们解决了5000万份文档&#xff0c;因为超出此数目我们仍然会考虑分片。 每次事件如下所示&#xff1a; {"_id" : ObjectId("5298a5a03b3…

scala-jdbc-scalike操作jdbc数据库

1, 引入maven依赖 <!-- 使用 sclaikeJDBC --><dependency><groupId>org.scalikejdbc</groupId><artifactId>scalikejdbc_2.11</artifactId><version>3.3.1</version></dependency><dependency><groupId>org…

day 17python 面对对象之继承

一&#xff1a;什么面向对象的继承&#xff1f; 比较官方的说法就是&#xff1a; 继承&#xff08;英语&#xff1a;inheritance&#xff09;是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B&#xff0c;就把这个A称为“B的子类别”&#xff0c;而把B称…

js 人民币小写金额转换为大写

function smalltoBIG(n) {var fraction [角, 分];var digit [零, 壹, 贰, 叁, 肆, 伍, 陆, 柒, 捌, 玖];var unit [[元, 万, 亿],[, 拾, 佰, 仟]];var head n < 0 ? 欠 : ;n Math.abs(n);var s ;for (var i 0; i < fraction.length; i ) {s (digit[Math.floor…

mybatis源码_Mybatis源码之SqlSession

SqlSession简介Mybatis是一个强大的ORM框架&#xff0c;它通过接口式编程为开发者屏蔽了传统JDBC的诸多不便&#xff0c;以简单的方式提供强大的扩展能力。其中的接口式编程就是指日常使用的Mapper接口&#xff0c;Mybatis借助动态代理实现了sql语句与Mapper的接口的动态绑定&a…

r语言kmodes_聚类分析——k-means算法及R语言实现

我们知道『物以类聚&#xff0c;人以群分』&#xff0c;这里并不是分类问题&#xff0c;而是聚类问题。两者主要区别在于&#xff0c;分类是将一组数据根据不同的类区分&#xff0c;已经知道有哪些类&#xff0c;也就是数据已经有了类的标签。而聚类是一种事先不知道有多少类&a…

VSCode安装jshint插件报错

Mac电脑上使用VSCode安装jshint插件时提示如下错误&#xff1a; Failed to load jshint library. Please install jshint in your workspace folder using npm install jshint or globally using npm install -g jshint and then press Retry. 按照提示&#xff0c;使用np…

按小时分组mysql 补齐_分组记录按小时或按天白天和mysql的

生成单列dates_hours表&#xff0c;该表包含在合理范围内(例如从1900到2200)的所有日期和小时数。 然后从此表执行LEFT JOIN到您当前的查询。对于这种技术要正确执行&#xff0c;你可能需要对索引列添加到您的表&#xff0c;它包含转换后的时间戳(你copied_timestamp转换为DATE…

项目学生:Spring数据的持久性

这是Project Student的一部分。 其他职位包括带有Jersey的Webservice Client&#xff0c;带有Jersey的 Webservice Server和业务层 。 RESTful webapp onion的最后一层是持久层。 持久层有两种哲学。 一个阵营将数据库视为一个简单的存储&#xff0c;并希望保持这一层非常薄。…

集合框架总结

2019作为新的一年开始&#xff0c;我也着手面试的准备。这篇的博客的主角集合--面试中都会出现的&#xff0c;所以今天特作此总结&#xff0c;也算是复习的成果的一个展示。在查看了许多的博客和源码后我决定将其分成3部分来总结。 三个部分分别是&#xff1a;集合的分类、各个…

python中自定义模块导入飘红_hadoop streaming 中跑python程序,自定义模块的导入

今天在做代码重构&#xff0c;以前将所有python文件放到一个文件夹下&#xff0c;上传到hadoop上跑&#xff0c;没有问题&#xff1b;不过随着任务的复杂性增加&#xff0c;感觉这样甚是不合理&#xff0c;于是做了个重构&#xff0c;建了好几个包存放不同功能的python文件&…

js 提取某()特殊字符串长度

// 提取特殊字符串长度&#xff08;scrstr 源字符串 armstr 特殊字符&#xff09; getStrCount: function(scrstr, armstr) {var count 0;while (scrstr.indexOf(armstr) > 1) {scrstr scrstr.replace(armstr, "")count ;}return count; } 更多专业前端知识&am…

运行jar包使用外部依赖

nohup java -Dloader.path"lib/" -Dfile.encodingutf-8 -jar test.jar > test.out 2>&1 & 转载于:https://www.cnblogs.com/hqzmss/p/9719380.html

调查内存泄漏第2部分–分析问题

这个小型系列的第一个博客介绍了如何创建一个非常泄漏的示例应用程序&#xff0c;以便我们可以研究解决服务器应用程序上基于堆的问题的技术。 它展示了Producer-Consumer模式的一个大问题&#xff0c;即消费者代码必须能够至少与生产者一样快&#xff08;如果不是更快&#xf…

es6 语法 (Decorator)

修饰器是一个函数&#xff0c;用来修改类的行为&#xff08;注意&#xff1a;1、函数 2、修改行为 3、对类进行操作&#xff09;{//修饰器函数定义 target:类本身&#xff0c;name名称&#xff0c;descriptor描述let readonly function(target, name, descriptor) {descript…

11小时 python自动化测试从入门到_从设计到开发Python接口自动化测试框架实战,资源教程下载...

课程名称从设计到开发Python接口自动化测试框架实战&#xff0c;资源教程下载课程简介&#xff1a;课程从接口基础知识入门&#xff0c;从抓包开始&#xff0c;到接口工具的运用&#xff0c;再到常见接口库、接口开发、Mock服务、unittest框架的运用&#xff0c;再讲解接口测试…