关于面试题java内存泄漏想到的(2)

7 篇文章 0 订阅
4 篇文章 0 订阅

Java资料下载地址:

http://blog.sina.com.cn/s/blog_67ba07d60100lolk.html

2java书籍找齐,一本一本查找关于内存管理的介绍,以及内存泄漏的介绍:

1、JavaLanguage Specification, Third Edition,此书编写风格有些类似于C++他爹写的The C++Programming Language,书中可运行的例子较少,不像java编程思想那么容易理解的例子,基本是能省略就省略,语言非常简介,层次非常清楚,内容非常全面详细无论大小知识,都很规范的给出说明,毕竟是规范嘛。关于内存泄漏没有说明,只有java垃圾回收机制:12.6小结(Finalization of Class Instances)

2、EffectiveJava , Second Edition第六条,消除过期引用的对象,很清楚的说明了这个问题,百度的面试题,以及JAVA高级工程师的面试题应该是源于此书

3、JavaConcurrency in Practice,简历上面写着精通java多线程编程,我只能说我做过java多线程的例子,不看此书不要说精通多线程编程。示例丰富,讲解详细。

4、JavaPuzzles: Traps, Pitfalls and Corner Cases也是非常适合出笔试题的一本书籍,java细节性的东西,稍不注意,编程就忽略掉的编程细节,只是不知道有没有关于内存泄漏的章节,大致看了一下,似乎没有,但是这是一本对java用编程经验的水平人的非常值得看的书籍,不看这些书籍,我想就是你再写十万行代码也是无用。

 

5、java编程思想,大家公认的java经典书籍,刚毕业那会儿一直看不懂,一直以为是很经典的书籍,现在看来,真的是入门级的书籍。不过是Java Language Specification的扩大版本,Java LanguageSpecification里面通常只有理论,没有示例,Thinking in java里面配上了示例,配上了浅显的语言。4.3节Cleanup: finalization and garbage collection有介绍java垃圾回收机制的

6Better,faster, lighter Java没有下载到能打开的格式,本人电脑chm格式的坏掉。

7Core Javathinking injava更深入些,但是内容不想后者那么全面,没有介绍内存有垃圾回收的章节

8The JavaVirtual Machine Specification  SUN公司的官方介绍java虚拟机的,自然也有垃圾回收,内存分配的事情,可惜没有下载到中文版,1.5 Garbage Collected Heap,垃圾回收堆,然后就几句话(总书也就84页)The Java heapis the runtime data area from which class instances (objects) are allocated.The Java language is designed to be garbage collected — it does not give theprogrammer the ability to deallocate objects explicitly.

Java does notpresuppose any particular kind of garbage collection; various algorithms may beused depending on system requirements

翻译一下吧:JAVA 的堆是运行时数据区域,类实例(对象)从这些堆中被收集。JAVA语言被设计为垃圾回收的机制,不给程序员明确分配对象的权力。Java不预定任何特别形式的垃圾集合,各种垃圾回收算法依赖于具体系统需求。翻译完了,得不到什么答案。

9、ExceptionHandling, Testing, and Debugging看名字应该是一本不错的书籍,但是没有下载到电子版

10、Java CodeConvention java官网的说明,基本都是变量怎么命名,类怎么命名之类的常规介绍

11、网上搜到一本书籍《深入理解Java虚拟机JVM高级特性与最佳实践》,感觉就是一本从网上东拉西扯的一本书,作者竟然用著,不过毕竟是中国人编写的书,语言还算容易理解,结合java编程思想看,还不错,里面就是把jvm之类的文章要点给整理了一下。

可以参考的书目:EffectiveJava,java编程思想,深入理解Java虚拟机JVM高级特性与最佳实践》

 

针对问题而来的书籍Effective Java

第二章 第六条Eliminate obsoleteobject references(消除过期的对象的引用)

内容不多,摘抄原文,并翻译一下:

When you switchfrom a language with manual memory management, such as C

or C++, to agarbage-collected language, your job as a programmer is made much

easier by thefact that your objects are automatically reclaimed when you’re

through withthem. It seems almost like magic when you first experience it. It can

easily lead tothe impression that you don’t have to think about memory management, but thisisn’t quite true.

Consider thefollowing simple stack implementation:

// Can you spot the"memory leak"?

 

当你从手动管理内存的语言例如,C,C++,转移到垃圾回收机制的语言上面,你作为程序员的工作由于以下的这个事情变的更容易,就是你的对象在你使用完之后,会自动的被回收利用。第一次这么使用的体验,让你觉得这是多么的神奇。这种体验很容易是你产生一个想法,“你再也不用管理内存了的”,这是个相当错误的想法。

考虑下面的小例子的栈实现。

你能找出里面的内存泄漏吗?

 

public classStack {

private Object[]elements;

private int size= 0;

private staticfinal int DEFAULT_INITIAL_CAPACITY = 16;

public Stack() {

elements = newObject[DEFAULT_INITIAL_CAPACITY];

}

public voidpush(Object e) {

ensureCapacity();

elements[size++]= e;

}

public Objectpop() {

if (size == 0)

throw new EmptyStackException();

returnelements[--size];

}

/**

* Ensure spacefor at least one more element, roughly

* doubling thecapacity each time the array needs to grow.

*/

private voidensureCapacity() {

if(elements.length == size)

elements =Arrays.copyOf(elements, 2 * size + 1);

}

}

 

拷贝程序运行,导入包

importjava.util.Arrays;

importjava.util.EmptyStackException;

size表示栈指针,默认大小16,构造函数按照默认值构造数组。入栈时候检查栈顶指针是不是最大了,如果是,则利用Arrays的copyOf,重新内存分配。

测试方法

    @Test

    public void testPop() {

       Stack stack = new Stack();

       String str = "fanjf";

       for (int i = 0; i < 20; i++) {

           stack.push(str + i);

       }

 

       for (int i = 0; i < 20; i++) {

 

           String strPop = (String) stack.pop();

           System.out.println(strPop);

       }

    }

 

There’s nothingobviously wrong with this program (but see Item 26 for a

genericversion). You could test it exhaustively, and it would pass every test with

flying colors,but there’s a problem lurking. Loosely speaking, the program has a

“memory leak,” whichcan silently manifest itself as reduced performance due to

increasedgarbage collector activity or increased memory footprint. In extreme

cases, suchmemory leaks can cause disk paging and even program failure with an

OutOfMemoryError,but such failures are relatively rare.

这个没有明显错误的程序(但是请看第26条的常见泛型的版,此处比较第26条的泛型版本只有pop 函数有差异elements[size] = null;对象弹出后进行了置空)。你不能耗尽一切的测试它,它总能带着胜利的旗帜通过你的测试,但是它真的有潜在问题。坦率的说,这个程序存在内存泄漏。随着垃圾回收活动增加或者内存占用的增加,这个程序会慢慢的展示出性能下降的情况。极端情况下,这种内存泄漏,会引起内存分页调度,甚至程序因为内存溢出而导致程序失败,尽管这种失败不常见。疑问1:怎么查出它是内存泄漏的?

 

So where is thememory leak? If a stack grows and then shrinks, the objects

that were poppedoff the stack will not be garbage collected, even if the program

using the stackhas no more references to them. This is because the stack maintains obsoletereferences to these objects. An obsolete reference is simply a reference thatwill never be dereferenced again. In this case, any references outside of the“active portion” of the element array are obsolete. The active portion consistsof the elements whose index is less than size.

然而哪里有内存泄漏呢?如果栈增加或者然后收缩,出栈的元素不会被垃圾回收器回收,甚至用栈的程序不再引用这些对象。因为栈维持者这些对象的旧的引用。一个过期的引用是指那些再也不被间接引用不会被解除的简单引用。这个例子中,任何数组元素有效(活动)部分之外的引用都是过期的。有效部分包含那些索引小于栈大小的元素

Memory leaks ingarbage-collected languages (more properly known as unintentional object retentions)are insidious. If an object reference is unintentionally retained, not only isthat object excluded from garbage collection, but so too are any objectsreferenced by that object, and so on. Even if only a few object references areunintentionally retained, many, many objects may be prevented from  being garbage collected, with potentiallylarge effects on performance.

内存泄漏在有垃圾回收机制的语言中是不明显的(更恰当的应该说是无意的对象保留),

如果对象被无意中保留了,那么不仅仅这些对象超出垃圾回收机制,这些对象引用的对象也超出垃圾回收机制了。甚至只是很少的对象引用被无意保留,但是越来越多的对象超越垃圾回收机制,最终会导致系统性能下降,最终对系统性能造成潜在的大影响。

The fix for thissort of problem is simple: null out references once they become obsolete. Inthe case of our Stackclass, the reference to an item becomes obsolete as soonas it’s popped off the stack.  Thecorrected version of the pop

处理这种问题是简单的。一旦引用过期,指向null就可以了,在我们的Stack类示例中个,当一个条目的引用当出栈之后就会过期。

当前版本的出栈代码如下

method lookslike this:

public Objectpop() {

if (size == 0)

throw newEmptyStackException();

Object result =elements[--size];

elements[size] =null; // Eliminate obsolete reference

return result;

}

An added benefitof nulling out obsolete references is that, if they are subsequentlydereferenced by mistake, the program will immediately fail with a NullPointerException,rather than quietly doing the wrong thing. It is always beneficial to detectprogrammingerrors as quickly as possible.

过期引用的null指向的清空过期引用另外一个好处是,如果以后他们被错误的解除引用,随之而来的错误指向,程序会立即报空指针异常,而不是做错的事情。这对尽可能快的发现程序错误是有利的。

When programmersare first stung by this problem, they may overcompensate by nulling out everyobject reference as soon as the program is finished using it.This is neithernecessary nor desirable, as it clutters up the program unnecessarily.Nullingout object references should be the exception rather than the norm.The best wayto eliminate an obsolete reference is to let the variable that contained thereference fall out of scope. This occurs naturally if you define each variablein the narrowest possible scope (Item 45).

当程序员第一次遇到这问题的时候,或许会过度的清空当程序使用完之后的每个对象的的引用。这是不必要的也是不合适的,因为这会使程序不必要的混乱起来。为什么?清空对象的引用不是java语言的规范,是例外情况。清楚过期引用的最好方式是让包含这个引用的变量放弃指向的区域。这个当你在定义每个变量在必须的最小的区域时候自然而然的就发生。

So when shouldyou null out a reference? What aspect of the Stack class makes it susceptibleto memory leaks? Simply put, it manages its own memory. The storage pool consistsof the elements of the elements array (the object reference cells, not theobjects themselves). The elements in the active portion of the array (asdefined earlier) are allocated, and those in the remainder of the array are free.The garbage collector has no way of knowing this; to the garbage collector,allof the object references in the elements array  are equally valid. Only the programmer knowsthat the inactive portion of the array is unimportant. The programmereffectively communicates this fact to the garbage collector by manually nullingout array elements as soon as they become part of the inactive portion.

然而,什么时候你应该置空一个引用呢?Stack类的哪些方面让它更容易内存泄漏呢?简单的入栈,它自己管理自己的内存。存储池包含着元素数组中的元素(是对象的引用不是对象本身)。在数组有效区域的元素(如早期定义的)是被分配的,数组其余的部分则是自由的。但是垃圾回收器并不知道这些情况,对于垃圾回收器数组中所有对象的引用都是同样有效的。只有程序员知道无效的数组区域是不重要的。当数组元素变到无效部分,程序员需要通知这个事情给垃圾回收器。

Generallyspeaking, whenever a class manages its own memory, the programmer should bealert for memory leaks. Whenever an element is freed, any object referencescontained in the element should be nulled out.

一般而言,无论什么时候一个类管理自己的内存,程序员都应该注意内存泄漏。无论什么时候,一个元素被释放,包含在元素中的任何对象引用应该被清空。

Another commonsource of memory leaks is caches.Once you put an object reference into a cache,it’s easy to forget that it’s there and leave it in the cache long after itbecomes irrelevant . There are several solutions to this problem. If you’relucky enough to implement a cache for which an entry is relevant exactly solong as there are references to its key outside of the cache, represent thecache as a Weak HashMap; entries will be removed automatically after theybecome obsolete. Remember that Weak HashMap is useful only if the desiredlifetime of cache

entries isdetermined by external references to the key, not the value.

另外一个内存泄漏的公共代码是缓存。一旦你放一个对象的引用到缓存中个,很容易忘记它,当不用它时候,就会长时间把它放在缓存。关于此问题有一些解决方案。如果你足够幸运去实现一个缓存,入口恰好相当重要的一个缓存,只要有key的引用在缓存之外,表现那个缓存是弱HashMap.入口将会在使用完之后自动被移除。记住了,弱HashMap是有用的,仅仅在期望的缓存生命周期,入口被外部引用的key所决定,而不是值。

内存泄漏另一个常见的来源是缓存。一旦你把对象引用放到缓存中,就很容易遗忘它,从而使得他再无用之后很长一段时间留在缓存中。对于这个问题有几种可能的解决方案。如果你正好要实现这样的缓存:只要在缓存之外存在对某项的键的引用个,该项就有意义,那就可以用Weak HaspMap(什么东西?)代表缓存;当缓存项过期之后,他们就自动被删除,记住只有当索要的缓存项的生命周期是有该键的外部引用而不是由值决定时,weakHashmap才有用处。

More commonly,the useful lifetime of a cache entry is less well defined, with entriesbecoming less valuable over time. Under these circumstances, the cache shouldoccasionally be cleansed of entries that have fallen into disuse. This can be doneby a background thread (perhaps a Timeror Scheduled Thread Pool Executor) or asa side effect of adding new entries to the cache. The LinkedHashMap classfacilitates the latter approach with its remove Eldest Entry method. For

moresophisticated caches, you may need to use java.lang.ref directly.

更常见的是:缓存项的生命周期是否有用是无法确定的,随着时间的推移,缓存项越来越无用。这种情况下,缓存应偶尔的清理废弃不用的项。这能通过一个后台线程(最好是定时器,或者是日程线程池执行者),或者另外一种方-添加新的项目到缓存中。LinkedHashMap类实现了后者促进后者逼近它移除边界的方法。更多复杂缓存,你可能需要直接使用java.lang.ref

java.lang.ref?

 A third common source of memory leaks islisteners and other callbacks.If you implement an API where clients registercallbacks but don’t deregister them explicitly, they will accumulate unless youtake some action. The best way to ensure that callbacks are garbage collectedpromptly is to store only weak references to them, for instance, by storingthem only as keys in a Weak HashMap.

另外的内存泄漏的公共代码是监听器和其他回调。如果你实现了一个API,客户端注册了回调,但是没有明确地注销,他们可能累积,直到你采取一些措施。确保垃圾回收器能够迅速回收的最好的方式是用弱引用保存他们,例如,存储这些对象的键仅仅在weak HashMap

仅仅保存他们到WeakHashMap中的键。

 

Because memoryleaks typically do not manifest themselves as obvious failures, they may remainpresent in a system for years. They are typically discovered only as a resultof careful code inspection or with the aid of a debugging tool known as a heapprofiler. Therefore, it is very desirable to learn to anticipate problems likethis before they occur and prevent them from happening.

由于内存泄漏非常典型的不能证明他们明显的错误,他们可能存在系统中数年。他们典型的被发现是由于代码审查,或者堆分析的调试工具(heap profiler)的帮助。然而,去学习这些提前知道这样的问题是非常值得的,尤其是在这些问题发生之前,预防他们发生

总结:JAVA内存泄漏原因只有一种,那就是过期的引用没有被清除,导致GC无法回收。本书介绍三种情况:1、全局集合持有临时对象,GC无法回收。2、缓存中的项目可能无用了,但是GC是无法回收的;更恶劣的情况是缓存还在不停的增加。3、监听器等反复的注册,使用回调函数,又没有明确的注销,导致对象一直都存在。

解决方案:1、全局集合持有临时对象的解决方案,是将全局对象不再使用的对象清空;2、缓存,思路有两个,一是缓存中的引用都用弱引用,二就是缓存清空,重新加载,可以写定时任务线程,定时清空缓存,重新加载,或者在加载新项目的时候考虑顺便清空;3、弱引用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值