LinuxSir.Org  
| 网站首页 | 论坛帮助 |

欢迎来到LinuxSir.Org!
您还未登录,请登录后查看论坛,或者点击论坛上方的注册链接注册新账号。


发表新主题 回复
精华主题  
主题工具
旧 08-06-19, 01:21 第 1 帖
自由狼-台风
 
 
 
注册会员  
  注册日期: Jun 2002
  我的住址: 广州 游走中
  帖子: 1,023
  精华: 12
 

标题: JVM垃圾回收判定问题。


所有与main()进程不再有任何引用关系的对象都被JVM视作垃圾,并会被适时回收;而 如果在需要废弃一个对象的时候,引用关系解除得不彻底,就会发生非预期的内存占用,即泄露。
  • 有没有什么工具能帮忙查出某个对象是否确实不再被引用、仅仅只是在等待GC来吃?
  • 或者有什么办法实时地显示出JVM内部所有对象之间的引用关系?
举例:
  1. 有一个全局范围的容器List<Object> l。
  2. 在局部范围内创建一个Object o,此时JVM从堆上划分一块内存创建对象XXX,交给o引用。
  3. 因为某些需要,或者特殊情况,o被添加进l里,则此时l也会引用对象XXX。
  4. 当o引用的对象XXX完成使命后,开发者为o赋了新值null,此时o不再引用对象XXX。
  5. 由于某些情况,开发者忽略了步骤3,或者步骤3是背着开发者进行的,又或者开发者没有权限从l中删除对象XXX,则l将一直引用对象XXX,而对象XXX所占据的内存空间在进程生命周期内永远无法释放。
  6. 凑巧,这段代码依据一定的规则循环执行,则l内会继续添加对对象YYY、ZZZ等的引用,并且在这些对象的使命完成后也因为同样的原因而不释放空间。
  7. 天长日久,系统越来越慢,终于有一天,OutOfMemoryError,成为注定的结局……
这种问题就发生在JDK的Logging系统中。而我正面临这样的问题。







__________________
写了一个小说。决定不要TJ,但进度很慢。
http://typhoon.rocklv.net/novels/TFW/Cross_The_Galaxy/index.html

此帖于 08-06-19 10:17 被 自由狼-台风 编辑.
  自由狼-台风 当前离线   回复时引用此帖
旧 08-06-19, 09:27 第 2 帖
jeff_yecn 帅哥
 
jeff_yecn 的头像
 
 
注册会员  
  注册日期: Feb 2004
  我的住址: Canada
  帖子: 180
  精华: 1
 

这种情况的发生是程序的错误造成的。Java 的垃圾收集机制只是简化程序员的负担,减少出错机会而已,但是没有一种编程语言能够避免程序员的错误。

回到这个例子,实现第 3 步的程序员显然有责任在代码中判断哪些对象不再需要放在 List 中,而把这个引用清除。







__________________
http://jeff.langcode.com
  jeff_yecn 当前离线   回复时引用此帖
旧 08-06-19, 10:12 第 3 帖
自由狼-台风
 
 
 
注册会员  
  注册日期: Jun 2002
  我的住址: 广州 游走中
  帖子: 1,023
  精华: 12
 

引用:
作者: jeff_yecn
这种情况的发生是程序的错误造成的。Java 的垃圾收集机制只是简化程序员的负担,减少出错机会而已,但是没有一种编程语言能够避免程序员的错误。

回到这个例子,实现第 3 步的程序员显然有责任在代码中判断哪些对象不再需要放在 List 中,而把这个引用清除。
这种状况在使用外来组件时尤其容易发生,比如JDK的Logger。

命名的Logger一旦创建,即驻留在内存中,即使这个Logger的使命已经完成也决不被GC吃掉;而程序员没有途径解除该命名空间的HashTable到Logger的引用。

匿名的Logger存在类似的问题,它会在Parent里留下引用,但API文档并没有明确说出这点,我是在一次偶然的Debug中发现这个问题的。当然,我可以用该Logger的setParent()方法把它的Parent设置为它自己以解除它和Parent之间的引用,但这样查找几乎是在黑盒子里探索迷宫,难免有疏漏;而一旦产生这样的疏漏,内存就泄漏了……
  自由狼-台风 当前离线   回复时引用此帖
旧 08-06-19, 18:31 第 4 帖
zbw76
 
zbw76 的头像
 
 
注册会员  
  注册日期: Mar 2003
  我的住址: d
  帖子: 379
  精华: 0
 

不知道jdk的loger是什么东西,没有用过,为什么不用log4j作日志那?重来没有碰到过这个问题。
  zbw76 当前离线   回复时引用此帖
旧 08-06-19, 19:04 第 5 帖
自由狼-台风
 
 
 
注册会员  
  注册日期: Jun 2002
  我的住址: 广州 游走中
  帖子: 1,023
  精华: 12
 

引用:
作者: zbw76
不知道jdk的loger是什么东西,没有用过,为什么不用log4j作日志那?重来没有碰到过这个问题。
  1. 因为需要。看Jarkata的Common-Logging不爽,需要自己写一套既能调用多种日志系统的日志托管体系,当然需要研究JDK自带的Logger。
  2. 即使我现在通过用Log4j而绕过这个问题,终究有一天我还是可能在别的场合面对这个问题,不如早点解决。

此帖于 08-06-20 15:53 被 自由狼-台风 编辑. 原因: 错别字
  自由狼-台风 当前离线   回复时引用此帖
旧 08-06-19, 22:00 第 6 帖
zbw76
 
zbw76 的头像
 
 
注册会员  
  注册日期: Mar 2003
  我的住址: d
  帖子: 379
  精华: 0
 

这个问题只能是程序逻辑解决,对象被一只指着是java的大忌,所以,不熟悉的lib不要随便用.
  zbw76 当前离线   回复时引用此帖
旧 08-06-19, 22:16 第 7 帖
zbw76
 
zbw76 的头像
 
 
注册会员  
  注册日期: Mar 2003
  我的住址: d
  帖子: 379
  精华: 0
 

看了一下jdk的LogManager觉得还是你自己的逻辑问题,它只是提供一个Loger的管理功能,应该重复使用log不应该是一只add log吧?贴出代码一起研究研究阿。
  zbw76 当前离线   回复时引用此帖
旧 08-06-19, 22:26 第 8 帖
zbw76
 
zbw76 的头像
 
 
注册会员  
  注册日期: Mar 2003
  我的住址: d
  帖子: 379
  精华: 0
 

public static synchronized Logger getLogger(String name) {
LogManager manager = LogManager.getLogManager();
Logger result = manager.getLogger(name);
if (result == null) {
result = new Logger(name, null);
manager.addLogger(result);
result = manager.getLogger(name);
}
return result;
}
这个是getLogger的实现,如果name有规划的话,系统会自动利用原来的log的,每个名字保留一个,这样应该不会有很多垃圾阿?
  zbw76 当前离线   回复时引用此帖
旧 08-06-19, 22:29 第 9 帖
zbw76
 
zbw76 的头像
 
 
注册会员  
  注册日期: Mar 2003
  我的住址: d
  帖子: 379
  精华: 0
 

不知道你的loger是怎么得到的,如果是用Logger.getLogger(name)取得的,应该没有问题,它不允许name=null的。
  zbw76 当前离线   回复时引用此帖
旧 08-06-20, 15:57 第 10 帖
自由狼-台风
 
 
 
注册会员  
  注册日期: Jun 2002
  我的住址: 广州 游走中
  帖子: 1,023
  精华: 12
 

引用:
作者: zbw76
看了一下jdk的LogManager觉得还是你自己的逻辑问题,它只是提供一个Loger的管理功能,应该重复使用log不应该是一只add log吧?贴出代码一起研究研究阿。
在一个因为与外来组件相关而无法使用Filter的特殊场合,需要使用生命周期较短的Logger。在这些Logger结束使命、需要被废弃的时候,问题产生了。命名的Logger被命名空间的HashTable所引用,而开发者没有途径解除这个引用。匿名的Logger与其Parent之间互相有引用关系。我还不清楚匿名Logger是否被其他涉及全局的东西所引用。

那段代码遵循GPL,当然可以贴出。但为了使问题明显、便于讨论,所需要的精炼工作量也不小,不方便贴出。

此帖于 08-06-20 16:01 被 自由狼-台风 编辑.
  自由狼-台风 当前离线   回复时引用此帖
旧 08-06-20, 16:06 第 11 帖
zbw76
 
zbw76 的头像
 
 
注册会员  
  注册日期: Mar 2003
  我的住址: d
  帖子: 379
  精华: 0
 

Logger log = Logger.getLogger(name);
log....
这样不能解决问题吗?

需要使用生命周期较短的Logger
=======================
什么意思?

其实java中很多这样的例子,就是用空间换时间,很多都是利用池的概念的,比如连接池,线程池。但是这些必须是可控得,如果控制不好,java照样会内存泄露。而内存泄露这个概念在java生态环境中,大部分人都不注意,所以问题还是比较严重的。我看到过好多人写的java服务程序每天重起一次的,要不资源就耗尽了。
  zbw76 当前离线   回复时引用此帖
旧 08-06-20, 18:25 第 12 帖
zbw76
 
zbw76 的头像
 
 
注册会员  
  注册日期: Mar 2003
  我的住址: d
  帖子: 379
  精华: 0
 

我晕,这个论坛服务器的时间用的那个时区的?怎么这么离谱?
  zbw76 当前离线   回复时引用此帖
旧 08-06-20, 23:53 第 13 帖
自由狼-台风
 
 
 
注册会员  
  注册日期: Jun 2002
  我的住址: 广州 游走中
  帖子: 1,023
  精华: 12
 

引用:
作者: zbw76
Logger log = Logger.getLogger(name);
log....
这样不能解决问题吗?

需要使用生命周期较短的Logger
=======================
什么意思?

其实java中很多这样的例子,就是用空间换时间,很多都是利用池的概念的,比如连接池,线程池。但是这些必须是可控得,如果控制不好,java照样会内存泄露。而内存泄露这个概念在java生态环境中,大部分人都不注意,所以问题还是比较严重的。我看到过好多人写的java服务程序每天重起一次的,要不资源就耗尽了。
我做的确实是个Web服务器端程序。需要为每次访问(包括来自不同IP,或者来自同一IP不同浏览器)创建日志文件,文件用访问开始时间戳+访问者IP+访问者用户名做唯一标识。

这个Web服务器端程序调用了一个外部组件,该外部组件中的日志处理代码不关心是谁或者是哪个访问在调用它,其所生成的日志中没有可用于标识访问来源的信息,所以Filter无用武之地,只得为每次访问开启一个Logger,待到访问结束时废弃该Logger。

因为并发访问数量不定,所以构建Logger池根本没有依据,不知道该事先创建多少个Logger。

打算等不那么忙以后研究一下JavaProfile,用它来看看我现在采取的办法是否会导致内存泄漏。
  自由狼-台风 当前离线   回复时引用此帖
旧 08-06-21, 13:58 第 14 帖
zbw76
 
zbw76 的头像
 
 
注册会员  
  注册日期: Mar 2003
  我的住址: d
  帖子: 379
  精华: 0
 

不能深入了解你的业务逻辑,所以不好说什么,不过我碰见过一个比较低级但是
却应该比较有用的解决办法:

1:首先,摸出程序在正常情况下大概多长时间会消耗资源比较严重;
2:然后给系统添加一个定时任务,比如每天的晚上1点重起服务。

虽然很弱智,但是确实很管用。能让服务稳定很多。
  zbw76 当前离线   回复时引用此帖
旧 08-06-21, 22:28 第 15 帖
自由狼-台风
 
 
 
注册会员  
  注册日期: Jun 2002
  我的住址: 广州 游走中
  帖子: 1,023
  精华: 12
 

引用:
作者: zbw76
不能深入了解你的业务逻辑,所以不好说什么,不过我碰见过一个比较低级但是
却应该比较有用的解决办法:

1:首先,摸出程序在正常情况下大概多长时间会消耗资源比较严重;
2:然后给系统添加一个定时任务,比如每天的晚上1点重起服务。

虽然很弱智,但是确实很管用。能让服务稳定很多。
Crontab每天执行“service tomcat restart”?现在还在初期的实验性编码阶段,我认为现在正是从源头解决这个问题的好时候。

也许我们现在做不出能可靠地24x365运行的系统,但至少我们要向这方面努力,有这方面的思维。我可不希望出自我手的产品出现每天或者隔天必须重新启动的情况。至少也得坚持一个星期到一个月吧。

出于时间分配,我不能去研究相关的分析工具了,但我委托一个同事去做,之后将和她共享心得。

此帖于 08-06-21 22:31 被 自由狼-台风 编辑.
  自由狼-台风 当前离线   回复时引用此帖
发表新主题 回复


主题工具

发帖规则
您 [不可以] 发表新主题
您 [不可以] 回复主题
您 [不可以] 上传附件
您 [不可以] 编辑您的帖子

已 [启用] BB 代码
已 [启用] 表情符号
已 [启用] IMG 代码
已 [禁用] HTML 代码
[论坛跳转…]


所有时间均为[北京时间]。现在的时间是 09:48


Powered by vBulletin 版本 3.6.8
版权所有 ©2000 - 2012, Jelsoft Enterprises Ltd.
官方中文技术支持: vBulletin 中文
版权所有 ©2002 - 2011, LinuxSir.Org