微机的发展历史简介发展到什么程度对照微机的发展历史简介技术指标

1.既然 Java 的垃圾回收机制能够自动的回收内存,怎么还会出现内存泄漏的情况呢?这个问题,我们需要知道 GC 在什么时候回收内存对象,什么样的内存对象会被 GC 认为是&不再使用&的。
Java中对内存对象的访问,使用的是引用的方式。在 Java 代码中我们维护一个内存对象的引用变量,通过这个引用变量的值,我们可以访问到对应的内存地址中的内存对象空间。在 Java 程序中,这个引用变量本身既可以存放堆内存中,又可以放在代码栈的内存中(与基本数据类型相同)。 GC 线程会从代码栈中的引用变量开始跟踪,从而判定哪些内存是正在使用的。如果 GC 线程通过这种方式,无法跟踪到某一块堆内存,那么 GC 就认为这块内存将不再使用了(因为代码中已经无法访问这块内存了)。
通过这种有向图的内存管理方式,当一个内存对象失去了所有的引用之后,GC 就可以将其回收。反过来说,如果这个对象还存在引用,那么它将不会被 GC 回收,哪怕是 Java 虚拟机抛出 OutOfMemoryError 。
2.java内存泄漏的根本原因是?
答:内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。
3.堆溢出:静态集合类中存储对象
java.lang.OutOfMemoryError: Java heap space
java.lang.StackOverflowError
5.代码栈是什么?
Vector v = new
Vector( 10 );
i = 1 ;i & 100 ; i ++ ){
Object o = new
}在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 o 。在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 o 引用置空。问题是当 o 引用被置空后,如果发生 GC ,我们创建的 Object 对象是否能够被 GC 回收呢?答案是否定的。因为, GC 在跟踪代码栈中的引用时,会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管 o 引用已经被置空,但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。如果在此循环之后, Object 对象对程序已经没有任何作用,那么我们就认为此 Java 程序发生了内存泄漏。7.内存泄漏在哪个领域比较常见?答:在移动设备对于内存和 CPU都有较严格的限制的情况下, Java 的内存溢出会导致程序效率低下、占用大量不需要的内存等问题。这将导致整个机器性能变差,严重的也会引起抛出 OutOfMemoryError ,导致程序崩溃。
&8.如何避免内存泄漏?
答:明确引用变量的生命周期,是方法内部的局部变量,还是类实例变量,与类实例生命周期相同的要声明为实例变量。
要避免这种情况下的内存泄露,要求我们以C/C++ 的内存管理思维来管理自己分配的内存。第一,是在声明对象引用之前,明确内存对象的有效作用域。在一个函数内有效的内存对象,应该声明为 local 变量,与类实例生命周期相同的要声明为实例变量&&以此类推。第二,在内存对象不再需要时,记得手动将其引用置空。
阅读(...) 评论()内存泄漏弄个明白
如果你参加面试,面试官经常会问到你的一个问题可能是:你在开发过程中,有过排除内存泄漏的经验吗?对于一个合格的Android/C/Java开发老手,这个问题想必已经深入你的心;若是一名新手或者一直对内存泄漏这个东西模模糊糊的工程师,你的答案可能让面试官并不满意,这里将从底到上对内存泄漏的原因、排查方法和一些经验为你做一次完整的解剖。
处理内存泄漏的问题是将软件做到极致的一个必须的步骤,尤其是那种将被用户高强度使用的软件。
一个简单的C和Android的例子
一个最简单的C的内存泄漏的例子:
char *ptr1 = (char *)malloc(<span STYLE="CoLor: #);
char *ptr2 = (char *)malloc(<span STYLE="CoLor: #);
ptr2 = ptr1;
free(ptr1)
这里最后发生了10个字节的内存泄漏,那么到底发生了什么?
首先各自分配了两块10个字节的内存,分别用叫ptr1和ptr2的指针指向这两块内存(就像是java中的引用),然后呢让ptr2也指向一开始ptr1指向的那块内存(这时候ptr1和ptr2都指向了ptr1一开始指向的那个10个字节的内存),最后用free将ptr1指向的那块内存给释放了——&结果就是一开始ptr2指向的那块内存发生了泄漏(没人用了却又回收不掉)
可能你会说Java有内存垃圾回收机制,不要考虑谁分配和释放的访问,那么下面这个例子就会让你明白你错了:
public class PendingOrderManager {
private static PendingOrderM
private Context mC
public PendingOrderManager(Context context) {
this.mContext =
public static PendingOrderManager getInstance(Context context) {
if (instance == null) {
instance = new PendingOrderManager(context);
   public void func(){
然后让你的某个Activity去使用这个PendingOrderManager单例,并且某个时候退出这个Activity:
//belong to some Activity
PendingOrderManager.getInstance(this).func();
这个时候内存泄漏已经发生:你退出了你的这个Activity本以为java的垃圾回收会将它释放,但实际上Activity一直被PendingOrderManager持有着。Acitivity这个Context被长生命周期个体(单例一旦被创建就是整个app的生命周期)持有导致了这个Context发生了内存泄漏。
这个例子和上面的例子是相通的,上面的C的例子因为忘记了手动执行free一个10字节内存导致内存泄漏。而下面这个例子是垃圾回收机制“故意忘记”了回收Context的内存而导致了内存泄漏。下面两节将对这个里面到底发生了什么进行说明。
静态、堆和栈
编译原理说软件内存分配的时候一般会放在三种位置:静态存储区域、堆和栈,他们的位置、功能、速度都各不相同,区别如下:
静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序整个运行期间都存在。它主要存放静态数据、全局static数据和常量
栈:就是CPU的寄存器(并不是内存),特点是容量很小但是速度最快,函数或者方法的的方法体内声明的变量或者指向对象的引用、局部变量即分配在这里,生命周期到该函数或者方法体尾部即止
堆:就是动态内存分配去(就是实体的内存RAM),C中malloc和fee,java中的new和垃圾回收直接操作的就是这里的区域,类的成员变量分配在这里
从上面即可看出静态存储区域是编译时已经分配好的,栈是CPU自动控制的,那么我们所讨论的内存泄漏的问题实际上就是分配在堆里面的内存出现了问题,一般问题在于两点:
快速不断的进行new操作。比如Android的自定义View的时候你在onDraw里面new出对象,就会导致这个自定义View的绘制特别卡,这是因为onDraw是快速重复执行的方法,在这个方法里面每次都new出对象会导致连续不断的new出新的对象,也导致gc也在不断的执行从而不断的回收堆内存。由于堆位于内存RAM上,这样子就导致了内存的不断的分配和回收消耗了CPU,同时导致了内存出现“空洞”(因为堆内存不是连续的)
忘记释放。如果你忘记了手动释放应该释放的内存,或者gc误判导致没有释放本应该释放的内存,那么久导致了内存泄漏。由于Android给一个app可在堆上(可以在AndroidManifest设置一个largeHeap="true"增大可分配量)分配的内存量是有限的,如果内存泄漏不断的发生,总有一天会消耗完毕,从而导致OOM
Java有了垃圾回收(GC)为什么任然后内存泄漏
在Java中,内存的分配是由程序完成的,而内存的释放是由垃圾收集器(Garbage
Collection,GC)完成的,程序员不需要通过调用函数来释放内存,但它只能回收无用并且不再被其它对象引用的那些对象所占用的空间。但是误判是经常发生的,有些内存实际上已经没有用处了,但是GC并不知道。这里简单介绍下GC的机制:
上面一节说过栈上的局部变量可以引用堆上的分配的内存,所以GC发生的时候,一般是遍历一下静态存储区、栈从而列出所有堆上被他们引用的内存(对象)集合,这些内存都是有个引用计数,那么除此之外,其他的内存就是没有被引用的(或者说引用计数归零),这些内存就是要被释放的,随后GC开始清理这些内存(对象)
那么这里第一节的两个例子就很好理解了,那个单例模式由于生命周期太长(可以把他看作一个虚拟的栈中的局部变量)并且一直引用了Context(即Activity),所以GC的时候发现这个Activity的引用计数还是大于1,所以回收内存的时候把他跳过,但实际上我们已经不需要这块内存了。这样就导致了内存泄漏。
Android使用弱引用和完美退出app的方法
从上面来看,内存泄漏因为对象被别人引用了而导致,java为了避免这种问题(假如你的单例模式必须要传入个Context),特地提供了几个特殊引用类型,其中一个叫做弱引用WeakReference,当它引用一个对象的时候,即使该WeakReference的生命周期更长,但是只要发生GC,它就立即释放所被引用的内存而不会继续持有。
这里有一个常用的例子:
通常我们会在自定义的Application中来记住app中创建的Activity,从而中途在某个Activity中需要完全退出app时可以完全的销毁所有已经打开的Activity,这里我们可以对自定义Application改造,让其只有一个对Activity的弱引用的HashMap,大致的代码如下:
public class CustomApplication extends Application {
private HashMap& activityList = new HashMap&();
private static CustomA
public static CustomApplication getInstance() {
public void addActivity(Activity activity) {
if (null != activity) {
L.d("********* add Activity " + activity.getClass().getName());
activityList.put(activity.getClass().getName(), new WeakReference&&(activity));
public void removeActivity(Activity activity) {
if (null != activity) {
L.d("********* remove Activity " + activity.getClass().getName());
activityList.remove(activity.getClass().getName());
public void exit() {
for (String key : activityList.keySet()) {
WeakReference activity = activityList.get(key);
if (activity != null && activity.get() != null) {
L.d("********* Exit " + activity.get().getClass().getSimpleName());
activity.get().finish();
System.exit(0);
android.os.Process.killProcess(android.os.Process.myPid());
我们在自定义的Activity的基类BaseActivity中的onCreate执行:
CustomApplication.getInstance().addActivity(this);
在BaseActivity的onDestroy中执行:
CustomApplication.getInstance().removeActivity(this);
这样子自定义Application就能不泄露的持有所有打开的Activity的引用,同时当你需要中途退出app的时候,只需要执行:
//完美退出程序方法
CustomApplication.getInstance().exit();
哪些情况会导致内存泄漏
到此你应该对内存泄漏的本质已经有所了解了,这里列举出一些会导致内存泄漏的地方,可以作为排查内存泄漏的一个checklist
某个集合类(List)被一个static变量引用,同时这个集合类没有删除自己内部的元素
单例模式持有外部本应该被释放的对象(第一节中那个例子)
Android特殊组件或者类忘记释放,比如:BraodcastReceiver忘记解注册、Cursor忘记销毁、Socket忘记close、TypedArray忘记recycle、callback忘记remove。如果你自己定义了一个类,最好不要直接将一个Activity类型作为他的属性,如果必须要用,要么处理好释放的问题,要么使用弱引用
Handler。只要 Handler 发送的 Message 尚未被处理,则该 Message 及发送它的 Handler
对象将被线程 MessageQueue 一直持有。由于 Handler 属于 TLS(Thread Local Storage)
变量, 生命周期和 Activity 是不一致的。因此这种实现方式一般很难保证跟 View 或者 Activity
的生命周期保持一致,故很容易导致无法正确释放。如上所述,Handler 的使用要尤为小心,否则将很容易导致内存泄露的发生。
Thread。如果Thread的run方法一直在循环的执行不停,而该Thread又持有了外部变量,那么这个外部变量即发生内存泄漏。
网络请求或者其他异步线程。之前Volley会有这样的一个问题,在Volley的response来到之前如果Activity已经退出了而且response里面含有Activity的成员变量,会导致该Activity发生内存泄漏,该问题一直没有找到合适的解决办法。不过看来Volley官网已经注意到这个问题了,目前最新的版本已经fix
使用leakcanary
之前Android开发通常使用MAT内存分析工具来排查heap的问题,之类的文章比较多,大家可以自己找。这里推荐一个叫做leakcanary的工具,他可以集成在你的代码里面。这个东西大家可以参考:
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。1.IE7/8 DOM对象或者ActiveX对象循环引用导致内存泄漏
  循环引用分为两种:
  第一种:多个对象循环引用
var a=new O
var b=new O
  第二种:循环引用自己
var a=new O
  对于ECMAScript 对象而言,只要没有其他对象引用对象 a、b,也就是说它们只是相互之间的引用,那么仍然会被垃圾收集系统识别并处理。
  但是,在 IE7、IE8 中,如果循环引用中的任何对象是 DOM 节点或者 ActiveX 对象,比如var a = document.getElementById("#a"),垃圾收集系统则不会发现它们之间的循环关系,因为IE的DOM回收机制和JS回收机制不是同一个。js回收机制分两种:标记清除和引用计数,引用计数对循环引用的垃圾回收会出现内存泄漏,而IE的DOM回收机制便是采用引用计数的。IE9+并不存在循环引用导致Dom内存泄露问题,可能是微软做了优化,或者Dom的回收方式已经改变。
下面摘自小苹果的
二、标记清除
js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为&进入环境&。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为&离开环境&。
function test(){
var a = 10 ; //被标记 ,进入环境
var b = 20 ; //被标记 ,进入环境
test(); //执行完毕 之后 a、b又被标离开环境,被回收。
  垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
  到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
三、引用计数
  引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。
function test(){
var a = {} ; //a的引用次数为0
var b = //a的引用次数加1,为1
var c =a; //a的引用次数再加1,为2
var b ={}; //a的引用次数减1,为1
  Netscape Navigator3是最早使用引用计数策略的浏览器,但很快它就遇到一个严重的问题:循环引用。循环引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。
function fn() {
var a = {};
var b = {};
  以上代码a和b的引用次数都是2,fn()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为a和b的引用次数不为0,所以不会被垃圾回收器回收内存,如果fn函数被大量调用,就会造成内存泄露。在IE7与IE8上,内存直线上升。
  我们知道,IE中有一部分对象并不是原生js对象。例如,其内存泄露DOM和BOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的js引擎采用标记清除策略来实现,但js访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。
var element = document.getElementById("some_element");
var myObject = new Object();
myObject.e =
element.o = myO
  这个例子在一个DOM元素(element)与一个原生js对象(myObject)之间创建了循环引用。其中,变量myObject有一个名为element的属性指向element对象;而变量element也有一个属性名为o回指myObject。由于存在这个循环引用,即使例子中的DOM从页面中移除,它也永远不会被回收。
  看上面的例子,有同学回觉得太弱了,谁会做这样无聊的事情,其实我们是不是就在做
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){};
  这段代码看起来没什么问题,但是obj引用了document.getElementById(&element&),而document.getElementById(&element&)的onclick方法会引用外部环境中的变量,自然也包括obj,是不是很隐蔽啊。
  解决办法
  最简单的方式就是自己手工解除循环引用,比如刚才的函数可以这样
myObject.element = null;
element.o = null;
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){};
  将变量设置为null意味着切断变量与它此前引用的值之间的连接。当垃圾回收器下次运行时,就会删除这些值并回收它们占用的内存。
  要注意的是,IE9+并不存在循环引用导致Dom内存泄露问题,可能是微软做了优化,或者Dom的回收方式已经改变
  在上面描述的循环引用例子
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){};
&  还可以理解成闭包循环引用导致的内存泄漏。怎么理解?
  首先obj是外部的一个对象,&obj.onclick定义的这个函数隐式的调用到了obj这个对象(obj.onclick函数中的this就是对象obj)。然后我们需要知道obj.onclick实际上是一个outerFunction外部的函数,为什么?DOM监听事件不可能是局部作用域的,是全局作用域的,明白了吧。所以DOM触发这个事件相当于是在函数outerFunction外部调用了obj.click(),而事件内部使用了outerFunction的变量obj,这就形成了一个闭包。IE7/IE8 DOM的引用计数永远无法回收这个DOM对象。无论如何,这都是DOM循环引用导致的内存泄漏,普通闭包是不会导致内存泄漏的。
  改成如下结构
window.onload=function outerFunction(){
var obj = document.getElementById("element");
$(obj).click(function innerFunction(){});
&  jQuery绑定事件最终都没有直接绑定到DOM对象上,而是使用jQuery缓存来绑定的。详见
  即使此时仍然会创建一个闭包,并且也会导致同前面一样的循环,但这里的代码却不会使&IE&发生内存泄漏。由于jQuery考虑到了内存泄漏的潜在危害,所以它会手动释放自己指定的所有事件处理程序(jQuery源代码$.fn.remove函数中有对节点的缓存释放的处理)。只要坚持使用jQuery的事件绑定方法,就无需为这种特定的常见原因导致的内存泄漏而担心。
  但是,这并不意味着我们完全脱离了险境。当对DOM元素进行其他操作时,仍然要处处留心。只要是将JavaScript对象指定给DOM元素,就可能在旧版本IE中导致内存泄漏。jQuery只是有助于减少发生这种情况的可能性。
  有鉴于此,jQuery为我们提供了另一个避免这种泄漏的工具。用.data()方法,将信息附加到DOM元素。由于这里的数据并非直接保存在扩展属性中(jQuery使用一个内部对象并通过它创建的ID来保存这里所说的数据),因此永远也不会构成引用循环,从而有效回避了内存泄漏问题。这种方式也就是jQuery事件绑定使用的方式。
2.基础的DOM泄漏
当原有的DOM被移除时,子结点引用没有被移除则无法回收。
var select = document.queryS
var treeRef = select('#tree');
var leafRef = select('#leaf');
//在COM树中leafRef是treeFre的一个子结点
select('body').removeChild(treeRef);//#tree不能被回收入,因为treeRef还在
&  解决方法:
treeRef = null;//tree还不能被回收,因为叶子结果leafRef还在
leafRef = null;//现在#tree可以被释放了
DOM 插入顺序导致内存泄漏
  当 动态创建的2 个不同范围的 DOM&对象附加到一起的时候,一个临时的对象会被创建。这个 DOM 对象改变范围到 document 时,那个临时对象就没用了,这个临时对象没有被回收将导致内存泄漏。如果我们一一将这两个DOM添加到原有的DOM&对象上就不会产生中间临时对象。详见的页面交叉泄露 Cross-Page Leaks。
3.timer定时器泄漏
var val = 0;
for (var i = 0; i & 90000; i++) {
var buggyObject = {
callAgain: function() {
var ref = this;
val = setTimeout(function() {
ref.callAgain();
}, 90000);
buggyObject.callAgain();
&  这个时候你无法回收buggyObject
//虽然你想回收但是timer还在
buggyObject = null;
&&  解决办法,先停止timer然后再回收
//解决方法,先停止定时器
clearTimeout(val);
buggyObject = null;
推荐内存泄漏文章:
理解和解决IE内存泄漏(中文翻译):/articles/2AZ3y2
理解和解决IE内存泄漏(英文原版):/en-us/library/bb250448.aspx
js内存泄露的几种情况:http://blog.csdn.net/li2274221/article/details/
  如果觉得本文不错,请点击右下方【推荐】!
阅读(...) 评论()

我要回帖

更多关于 微机系统的技术指标 的文章

 

随机推荐