热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

Java资料下载地址:http:blog.sina.com.cnsblog_67ba07d60100lolk.html2、java书籍找齐,一本一本查找关于内存管理的介绍,以及内存

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、弱引用。


推荐阅读
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了GregorianCalendar类的基本信息,包括它是Calendar的子类,提供了世界上大多数国家使用的标准日历系统。默认情况下,它对应格里高利日历创立时的日期,但可以通过调用setGregorianChange()方法来更改起始日期。同时,文中还提到了GregorianCalendar类为每个日历字段使用的默认值。 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • 生产环境下JVM调优参数的设置实例
     正文前先来一波福利推荐: 福利一:百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。福利二 ... [详细]
author-avatar
我爱我19930515
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有