精华内容
下载资源
问答
  • 出现内存问题,我们可以采用系统自带的工具进行内存检测,可是很多用户不知道Win7怎么使用自带工具进行内存检测,为此小编赶紧整理了以下教程帮助大家,大家赶紧看看吧!方法/步骤:1、对于Win7系统,在开始界面搜索...

    电脑使用久了,总是会出现各种各样的问题,其中比较常见的就是内存问题了。出现内存问题,我们可以采用系统自带的工具进行内存检测,可是很多用户不知道Win7怎么使用自带工具进行内存检测,为此小编赶紧整理了以下教程帮助大家,大家赶紧看看吧!

    方法/步骤:

    1、对于Win7系统,在开始界面搜索‘内存’来查询工具。

    2、查询结果如下,选择‘Windows内存诊断’。

    3、对于Win8/Win8.1系统,按搜索快捷键Win+Q,输入‘内存’来查询工具。

    4、在查询结果里边选择‘诊断计算机的内存问题’。

    5、选择Windows内存诊断工具之后,会有选项出现,询问:

    (1)立即重新启动并检查内问题。

    (2)下次启动计算机检查问题。

    如果选择1,请先保存自己的工作;如果选择2,会在下次重启计算机时检查。

    6、如下界面是内存检查界面,检查总共分两个阶段,如果内存有问题会提示。

    7、检查完成之后会自动重启到系统,在系统底下会提示检查结果。

    Win7使用自带工具进行内存检测的方法就讲到这里了,我们首先点击开始菜单,然后搜索‘内存’,之后点击‘Windows内存诊断’,最后在弹出来的框中选择自己需要的选项,重启之后电脑就会提示检查结果了。

    展开全文
  • Google memchek 内存检测

    2012-05-11 08:31:25
    最近在看了一下内存检测,在http://code.google.com/p/memcheck/可以找到,文章有源码和test,简单的几句介绍,刚开始没怎么看懂,自己根据使用说明用了下立马就能上手,显示结果最好用cmd调用生成的exe,能清楚打印...

    最近在看了一下内存检测,在http://code.google.com/p/memcheck/可以找到,文章有源码和test,简单的几句介绍,刚开始没怎么看懂,自己根据使用说明用了下立马就能上手,显示结果最好用cmd调用生成的exe,能清楚打印mem leaking的情况。

    主要代码:

    bool mc_checkmem(){
    bool memLeaked=false;
    for(int i=0;i<MC_HASHTABLESIZE;++i){
    mc_block_node_t* ptr=pTable[i];
    if(ptr==NULL){
    continue;
    }
    memLeaked=true;
    while(ptr){
    printf("Leaked object (size %u ",ptr->size);
    #ifdef MC_ADDRESS
    printf("at 0x%p ",(ptr+1));
    #endif
    #ifdef MC_FILENAME
    printf("in %s",ptr->file);
    #endif
    #ifdef MC_FUNCTION
    printf(":%s()",ptr->func);
    #endif
    #ifdef MC_LINE_NUMBER
    printf(":%d",ptr->line);
    #endif
    printf(")\n");
    ptr=ptr->next;
    }
    }
    return memLeaked;
    }

    程序通过建立以个内存块的hash表,来记录内存的使用情况,并打印出来,结果很详细。本人建立一个DLL并加入了memcheck程序。DLL中内存泄漏代码如下:

    double MyMathFuncs::Divide(double a,double b)
    {
    int* c=new int[10];
    //printf("main():b==0x%p\n",b);
    for(int i=0;i<10;++i){
    c[i]=i;
    }
    if(b == 0)
    {
    throw new invalid_argument("b cannot be zero");
    }

    return a / b;
    }

    结果为:


    程序清楚打印出是怎么分配内存,分配内存的大小,以及位置和内存泄漏的情况。

    展开全文
  • 没有经验的程序员经常认为Java的自动垃圾回收完全使他们免于担心内存管理。这是一个常见的误解:虽然垃圾收集器做得很好,但即使是...更糟糕的是,检测这些泄漏可能很困难:静态分析通常很难精确识别这些冗余引用...

    没有经验的程序员经常认为Java的自动垃圾回收完全使他们免于担心内存管理。这是一个常见的误解:虽然垃圾收集器做得很好,但即使是最好的程序员也完全有可能成为严重破坏内存泄漏的牺牲品。让我解释一下。

    当不必要地维护不再需要的对象引用时,会发生内存泄漏。这些泄漏很糟糕。首先,当程序消耗越来越多的资源时,它们会对计算机施加不必要的压力。更糟糕的是,检测这些泄漏可能很困难:静态分析通常很难精确识别这些冗余引用,现有的泄漏检测工具会跟踪和报告有关单个对象的细粒度信息,产生难以解释且缺乏精确度的结果。

    换句话说,泄漏要么太难以识别,要么使用太过具体而无用术语来识别。

    实际上有四类内存问题具有相似和重叠的特征,但原因和解决方案各不相同:

    • Performance(性能):通常与过多的对象创建和删除,垃圾收集的长时间延迟,过多的操作系统页面交换等相关联。
    • Resource constraints(资源约束):当可用内存很少或内存过于分散而无法分配大对象时 - 这可能是本机的,或者更常见的是与Java堆相关。
    • Java heap leaks(java堆泄漏):经典的内存泄漏,Java对象在不释放的情况下不断创建。这通常是由潜在对象引用引起的。
    • Native memory leaks(本机内存泄漏):与Java堆之外的任何不断增长的内存利用率相关联,例如由JNI代码,驱动程序甚至JVM分配。

    在这个内存管理教程中,我将专注于Java堆漏洞,并概述一种基于Java VisualVM报告检测此类泄漏的方法,并利用可视化界面在运行时分析基于Java技术的应用程序。

    但在您可以预防和发现内存泄漏之前,您应该了解它们的发生方式和原因。(注意:如果你能很好地处理错综复杂的内存泄漏,你可以跳过。)

    1. 内存泄漏:基础

    对于初学者来说,将内存泄漏视为一种疾病,将Java的OutOfMemoryError(简称OOM)视为一种症状。但与任何疾病一样,并非所有OOM都意味着内存泄漏:由于生成大量局部变量或其他此类事件,OOM可能会发生。另一方面,并非所有内存泄漏都必然表现为OOM,特别是在桌面应用程序或客户端应用程序(没有重新启动时运行很长时间)的情况下。

    将内存泄漏视为疾病,将OutOfMemoryError视为症状。但并非所有OutOfMemoryErrors都意味着内存泄漏,并非所有内存泄漏都表现为OutOfMemoryErrors。

    为什么这些泄漏如此糟糕?除此之外,程序执行期间泄漏的内存块通常会降低系统性能,因为分配但未使用的内存块必须在系统耗尽空闲物理内存时进行换出。最终,程序甚至可能耗尽其可用的虚拟地址空间,从而导致OOM。

    2. 解密OutOfMemoryError

    如上所述,OOM是内存泄漏的常见指示。实质上,当没有足够的空间来分配新对象时,会抛出错误。当垃圾收集器找不到必要的空间,并且堆不能进一步扩展,会多次尝试。因此,会出现错误以及堆栈跟踪。

    诊断OOM的第一步是确定错误的实际含义。这听起来很清楚,但答案并不总是那么清晰。例如:OOM是否是因为Java堆已满而出现,还是因为本机堆已满?为了帮助您回答这个问题,让我们分析一些可能的错误消息:

    • java.lang.OutOfMemoryError: Java heap space
    • java.lang.OutOfMemoryError: PermGen space
    • java.lang.OutOfMemoryError: Requested array size exceeds VM limit
    • java.lang.OutOfMemoryError: request bytes for . Out of swap space?
    • java.lang.OutOfMemoryError: (Native method)

    2.1.“Java heap space”

    此错误消息不一定意味着内存泄漏。实际上,问题可能与配置问题一样简单。

    例如,我负责分析一直产生这种类型的OutOfMemoryError的应用程序。经过一番调查后,我发现罪魁祸首是阵列实例化,因为需要太多的内存;在这种情况下,并不是应用程序的错,而是应用程序服务器依赖于默认的堆太小了。我通过调整JVM的内存参数解决了这个问题。

    在其他情况下,特别是对于长期存在的应用程序,该消息可能表明我们无意中持有对象的引用,从而阻止垃圾收集器清理它们。这时Java语言等同于内存泄漏。(注意:应用程序调用的API也可能无意中持有对象引用。)

    这些“Java堆空间”OOM的另一个潜在来源是使用finalizers。如果类具有finalize方法,则在垃圾收集时该类型的对象不会被回收。而是在垃圾收集之后,稍后对象将排队等待最终确定。在Sun实现中,finalizers由守护线程执行。如果finalizers线程无法跟上finalization队列,那么Java堆可能会填满并且可能抛出OOM。

    2.2. “PermGen space”

    此错误消息表明永久代已满。永久代是存储类和方法对象的堆的区域。如果应用程序加载了大量类,则可能需要使用-XX:MaxPermSize选项增加永久代的大小。

    Interned java.lang.String对象也存储在永久代中。java.lang.String类维护一个字符串池。调用实习方法时,该方法检查池以查看是否存在等效字符串。如果是这样,它由实习方法返回;如果没有,则将字符串添加到池中。更准确地说,java.lang.String.intern方法返回一个字符串的规范表示;结果是对该字符串显示为文字时将返回的同一个类实例的引用。如果应用程序实例化大量字符串,则可能需要增加永久代的大小。

    注意:您可以使用jmap -permgen命令打印与永久生成相关的统计信息,包括有关内部化String实例的信息。

    2.3.“Requested array size exceeds VM limit”

    此错误表示应用程序(或该应用程序使用的API)尝试分配大于堆大小的数组。例如,如果应用程序尝试分配512MB的数组但最大堆大小为256MB,则将抛出此错误消息的OOM。在大多数情况下,问题是配置问题或应用程序尝试分配海量数组时导致的错误。

    2.4. “Request bytes for . Out of swap space?”

    此消息似乎是一个OOM。但是,当本机堆的分配失败并且本机堆可能将被耗尽时,HotSpot VM会抛出此异常。消息中包括失败请求的大小(以字节为单位)以及内存请求的原因。在大多数情况下,是报告分配失败的源模块的名称。

    如果抛出此类型的OOM,则可能需要在操作系统上使用故障排除实用程序来进一步诊断问题。在某些情况下,问题甚至可能与应用程序无关。例如,您可能会在以下情况下看到此错误:

    • 操作系统配置的交换空间不足。
    • 系统上的另一个进程是消耗所有可用的内存资源。

    由于本机泄漏,应用程序也可能失败(例如,如果某些应用程序或库代码不断分配内存但无法将其释放到操作系统)。

    2.5. Native method

    如果您看到此错误消息并且堆栈跟踪的顶部框架是本机方法,则该本机方法遇到分配失败。此消息与上一个消息之间的区别在于,在JNI或本机方法中检测到Java内存分配失败,而不是在Java VM代码中检测到。

    如果抛出此类型的OOM,您可能需要在操作系统上使用实用程序来进一步诊断问题。

    2.6. Application Crash Without OOM

    有时,应用程序可能会在从本机堆分配失败后很快崩溃。如果您运行的本机代码不检查内存分配函数返回的错误,则会发生这种情况。

    例如,如果没有可用内存,malloc系统调用将返回NULL。如果未检查malloc的返回,则应用程序在尝试访问无效的内存位置时可能会崩溃。根据具体情况,可能很难定位此类问题。

    在某些情况下,致命错误日志或崩溃转储的信息就足以诊断问题。如果确定崩溃的原因是某些内存分配中缺少错误处理,那么您必须找到所述分配失败的原因。与任何其他本机堆问题一样,系统可能配置了但交换空间不足,另一个进程可能正在消耗所有可用内存资源等。

    3. 泄漏诊断

    在大多数情况下,诊断内存泄漏需要非常详细地了解相关应用程序。警告:该过程可能很长并且是迭代的。

    我们寻找内存泄漏的策略将相对简单:

    1. 识别症状
    2. 启用详细垃圾回收
    3. 启用分析
    4. 分析踪迹

    3.1. 识别症状

    正如所讨论的,在许多情况下,Java进程最终会抛出一个OOM运行时异常,这是一个明确的指示,表明您的内存资源已经耗尽。在这种情况下,您需要区分正常的内存耗尽和泄漏。分析OOM的消息并尝试根据上面提供的讨论找到罪魁祸首。

    通常,如果Java应用程序请求的存储空间超过运行时堆提供的存储空间,则可能是由于设计不佳导致的。例如,如果应用程序创建映像的多个副本或将文件加载到数组中,则当映像或文件非常大时,它将耗尽存储空间。这是正常的资源耗尽。该应用程序按设计工作(虽然这种设计显然是愚蠢的)。

    但是,如果应用程序在处理相同类型的数据时稳定地增加其内存利用率,则可能会发生内存泄漏。

    3.2. 启用详细垃圾收集

    断言确实存在内存泄漏的最快方法之一是启用详细垃圾回收。通常可以通过检查verbosegc输出中的模式来识别内存约束问题。

    具体来说,-verbosegc参数允许您在每次垃圾收集(GC)过程开始时生成跟踪。也就是说,当内存被垃圾收集时,摘要报告会打印到标准错误,让您了解内存的管理方式。

    这是使用-verbosegc选项生成的一些典型输出:

    cd7626a8c1e7f2fe0fc2cb6f9b152247.png

    此GC跟踪文件中的每个块(或节)按递增顺序编号。要理解这种跟踪,您应该查看连续的分配失败节,并查找随着时间的推移而减少的释放内存(字节和百分比),同时总内存(此处,19725304)正在增加。这些是内存耗尽的典型迹象。

    3.3. 启用分析

    不同的JVM提供了生成跟踪文件以反映堆活动的不同方法,这些方法通常包括有关对象类型和大小的详细信息。这称为分析堆。

    3.4. 分析路径

    本文重点介绍Java VisualVM生成的跟踪。跟踪可以有不同的格式,因为它们可以由不同的Java内存泄漏检测工具生成,但它们背后的想法总是相同的:在堆中找到不应该存在的对象块,并确定这些对象是否累积而不是释放。特别感兴趣的是每次在Java应用程序中触发某个事件时已知的临时对象。应该仅存少量,但存在许多对象实例,通常表示应用程序出现错误。

    最后,解决内存泄漏需要您彻底检查代码。了解对象泄漏的类型可能对此非常有用,并且可以大大加快调试速度。

    4. 垃圾收集如何在JVM中运行?

    在我们开始分析具有内存泄漏问题的应用程序之前,让我们首先看看垃圾收集在JVM中的工作原理。

    JVM使用一种称为跟踪收集器的垃圾收集器,它基本上通过暂停它周围的世界来操作,标记所有根对象(由运行线程直接引用的对象),并遵循它们的引用,标记它沿途看到的每个对象。

    Java基于分代假设-实现了一种称为分代垃圾收集器的东西,该假设表明创建的大多数对象被快速丢弃,而未快速收集的对象可能会存在一段时间。

    基于此假设,[Java将对象分为多代](http://www.oracle.com/technetwork/java/gc-tuning-5-138395.html#1.1. Generations|outline)。这是一个视觉解释:

    c4eede2dbf218c8a35fbbb3c92993040.png
    • Young Generation -这是对象的开始。它有两个子代
      • Eden Space -对象从这里开始。大多数物体都是在Eden Space中创造和销毁的。在这里,GC执行Minor GCs,这是优化的垃圾收集。执行Minor GC时,对仍然需要的对象的任何引用都将迁移到其中一个survivors空间(S0或S1)。
      • Survivor Space (S0 and S1)-幸存Eden Space的对象最终来到这里。其中有两个,在任何给定时间只有一个正在使用(除非我们有严重的内存泄漏)。一个被指定为空,另一个被指定为活动,与每个GC循环交替。
    • Tenured Generation -也被称为老年代(图2中的旧空间),这个空间容纳存活较长的对象,使用寿命更长(如果它们活得足够长,则从Survivor空间移过来)。填充此空间时,GC会执行完整GC,这会在性能方面降低成本。如果此空间无限制地增长,则JVM将抛出OutOfMemoryError - Java堆空间。
    • Permanent Generation -作为与终身代密切相关的第三代,永久代是特殊的,因为它保存虚拟机所需的数据,以描述在Java语言级别上没有等价的对象。例如,描述类和方法的对象存储在永久代中。

    Java足够聪明,可以为每一代应用不同的垃圾收集方法。使用名为Parallel New Collector的跟踪复制收集器处理年轻代。这个收集器阻止了这个世界,但由于年轻一代通常很小,所以暂停很短暂。

    有关JVM代及其工作原理的更多信息,请查阅Memory Management in the Java HotSpot™ Virtual Machine 。

    5. 检测内存泄漏

    要查找内存泄漏并消除它们,您需要合适的内存泄漏工具。是时候使用Java VisualVM检测并删除此类泄漏。

    5.1. 使用Java VisualVM远程分析堆

    VisualVM是一种工具,它提供了一个可视化界面,用于查看有关基于Java技术的应用程序运行时的详细信息。

    使用VisualVM,您可以查看与本地应用程序和远程主机上运行的应用程序相关的数据。您还可以捕获有关JVM软件实例的数据,并将数据保存到本地系统。

    为了从Java VisualVM的所有功能中受益,您应该运行Java平台标准版(Java SE)版本6或更高版本。

    Related: Why You Need to Upgrade to Java 8 Already

    5.2. 为JVM启用远程连接

    在生产环境中,通常很难访问运行代码的实际机器。幸运的是,我们可以远程分析我们的Java应用程序。

    首先,我们需要在目标机器上授予自己JVM访问权限。为此,请使用以下内容创建名为jstatd.all.policy的文件:

    grant codebase "file:${java.home}/../lib/tools.jar"
       permission java.security.AllPermission;
    };

    创建文件后,我们需要使用jstatd - Virtual Machine jstat Daemon工具启用与目标VM的远程连接,如下所示:

    创建文件后,我们需要使用jstatd - Virtual Machine jstat Daemon工具启用与目标VM的远程连接,如下所示:

    jstatd -p <PORT_NUMBER>-J-Djava.security.policy=<PATH_TO_POLICY_FILE>

    例如:

    jstatd -p 1234-J-Djava.security.policy=D:jstatd.all.policy

    通过在目标VM中启动jstatd,我们能够连接到目标计算机并远程分析应用程序的内存泄漏问题。

    5.3. 连接到远程主机

    在客户端计算机中,打开提示并键入jvisualvm以打开VisualVM工具。

    接下来,我们必须在VisualVM中添加远程主机。当目标JVM启用以允许来自具有J2SE 6或更高版本的另一台计算机的远程连接时,我们启动Java VisualVM工具并连接到远程主机。如果与远程主机的连接成功,我们将看到在目标JVM中运行的Java应用程序,如下所示:

    要在应用程序上运行内存分析器,我们只需在侧面板中双击其名称即可。

    现在我们已经设置了内存分析器,让我们研究一个内存泄漏问题的应用程序,我们称之为MemLeak。

    6. MemLeak

    当然,有很多方法可以在Java中创建内存泄漏。为简单起见,我们将一个类定义为HashMap中的键,但我们不会定义equals()和hashcode()方法。

    HashMap是Map接口的哈希表实现,因此它定义了键和值的基本概念:每个值都与唯一键相关,因此如果给定键值对的键已经存在于HashMap,它的当前值被替换。

    我们的密钥类必须提供equals()和hashcode()方法的正确实现。没有它们,就无法保证会生成一个好的密钥。

    通过不定义equals()和hashcode()方法,我们一遍又一遍地向HashMap添加相同的键,而不是按原样替换键,HashMap不断增长,无法识别这些相同的键并抛出OutOfMemoryError 。

    MemLeak类:

    package com.post.memory.leak;
    
    import java.util.Map;
    
    public class MemLeak {
     public final String key;
    
     public MemLeak(String key) {
     this.key =key;
     }
    
     public static void main(String args[]) {
     try {
     Map map = System.getProperties();
     for(;;) {
                    map.put(new MemLeak("key"), "value");
     }
     } catch(Exception e) {
                e.printStackTrace();
     }
     }
    } 

    注意:内存泄漏不是由于第14行的无限循环:无限循环可能导致资源耗尽,但不会导致内存泄漏。如果我们已经正确实现了equals()和hashcode()方法,那么即使使用无限循环,代码也能正常运行,因为我们在HashMap中只有一个元素。

    (对于那些感兴趣的人,这里有一些(故意)产生泄漏的替代方法。)

    7. 使用Java VisualVM

    使用Java VisualVM,我们可以对Java Heap进行内存监视,并确定其行为是否存在内存泄漏。

    这是刚刚初始化后MemLeak的Java堆分析器的图形表示(回想一下我们对各代的讨论):

    f661c6c689291caeaf91d6b19c6881e0.png

    仅仅30秒之后,老年代几乎已满,表明即使使用Full GC,老年代也在不断增长,这是内存泄漏的明显迹象。

    检测此泄漏原因的一种方法如下图所示(单击放大),使用带有heapdump的Java VisualVM生成。在这里,我们看到50%的Hashtable $ Entry对象在堆中,而第二行指向MemLeak类。因此,内存泄漏是由MemLeak类中使用的哈希表引起的。

    b7f51d223d18f86ddde3f25a3c537eee.png

    最后,在OutOfMemoryError之后观察Java Heap,其中Young和Old代完全填满。

    ec36f7ad81af2b23b360c9d04b265c9c.png

    8. 结束语

    内存泄漏是最难解决的Java应用程序问题之一,因为症状多种多样且难以重现。在这里,我们概述了一种逐步发现内存泄漏并确定其来源的方法。但最重要的是,仔细阅读您的错误消息并注意堆栈跟踪 - 并非所有泄漏都像它们出现的那样简单。

    9. 附录

    与Java VisualVM一起,还有其他几种可以执行内存泄漏检测的工具。许多泄漏检测器通过拦截对存储器管理例程的调用在库级别操作。例如,HPROF是一个与Java 2平台标准版(J2SE)捆绑在一起的简单命令行工具,用于堆和CPU分析。可以直接分析HPROF的输出,或将其用作JHAT等其他工具的输入。当我们使用Java 2 Enterprise Edition(J2EE)应用程序时,有许多堆转储分析器解决方案更友好,例如IBM Heapdumps for Websphere应用程序服务器。

    原文:https://www.toptal.com/java/hunting-memory-leaks-in-java
    作者:Jose Ferreirade Souza Filho
    译者:Emma
    排版:锅外得大佬
    展开全文
  • 检测内存泄漏

    千次阅读 2010-02-26 15:32:00
    最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他指点一...

    最近看了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他指点一、二,然而未果。没有办法,最后我一头栽进 MSDN 库狂搜了一把,功夫不负有心人,我搜出很多有关这方面的资料,没过多久我便基本上就找到了答案......

    首先,检测内存泄漏的基本工具是调试器和 CRT 调试堆函数。为了使用调试堆函数,必须在要检测内存泄漏和调试的程序中添加下面的语句:

    #define _CRTDBG_MAP_ALLOC #include<stdlib.h> #include<crtdbg.h> #include "debug_new.h"

    MSDN 如是说:“必须保证上面声明的顺序,如果改变了顺序,可能不能正常工作。”至于这是为什么,我们不得而知。MS 的老大们经常这样故弄玄虚。

    针对非 MFC 程序,再加上周星星的头文件:(1)debug_new.h,当然如果不加这一句,也能检测出内存泄漏,但是你无法确定在哪个源程序文件中发生泄漏;(2)我们来模拟下MFC做的事情。看下例:

    inline void EnableMemLeakCheck()
    {
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
    }

    #ifdef _DEBUG
    #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
    #endif

    void main()
    {
    EnableMemLeakCheck();
    int* leak = new int[10];
    }

    。Output 输出只告诉你在 crtsdb.h 中的某个地方有内存泄漏。我测试时 REG_DEBUG_NEW 没有起作用。加不加这个宏都可以检测出发生内存分配泄漏的文件。

    其次,一旦添加了上面的声明,你就可以通过在程序中加入下面的代码来报告内存泄漏信息了:

    _CrtDumpMemoryLeaks(); 这就这么简单。我在周星星的例子代码中加入这些机关后,在 VC++ 调试会话(按 F5 调试运行) Output 窗口的 Debug 页便看到了预期的内存泄漏 dump。该 dump 形式如下:

    Detected memory leaks! Dumping objects -> c:/Program Files/.../include/crtdbg.h(552) : {45} normal block at 0x00441BA0, 2 bytes long. Data: <AB> 41 42 c:/Program Files/.../include/crtdbg.h(552) : {44} normal block at 0x00441BD0, 33 bytes long. Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD c:/Program Files/.../include/crtdbg.h(552) : {43} normal block at 0x00441C20, 40 bytes long. Data: < C > E8 01 43 00 16 00 00 00 00 00 00 00 00 00 00 00 Object dump complete.

    更具体的细节请参考本文附带的源代码文件。

    下面是我看过 MSDN 资料后,针对“如何使用 CRT 调试功能来检测内存泄漏?”的问题进行了一番编译和整理,希望对大家有用。如果你的英文很棒,那就不用往下看了,建议直接去读 MSDN 库中的技术原文。

    C/C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:“最大的长处也可能成为最大的弱点”,那么 C/C++ 应用程序正好印证了这句话。在 C/C++ 应用程序开发过程中,动态分配的内存处理不当是最常见的问题。其中,最难捉摸也最难检测的错误之一就是内存泄漏,即未能正确释放以前分配的内存的错误。偶尔发生的少量内存泄漏可能不会引起我们的注意,但泄漏大量内存的程序或泄漏日益增多的程序可能会表现出各种 各样的征兆:从性能不良(并且逐渐降低)到内存完全耗尽。更糟的是,泄漏的程序可能会用掉太多内存,导致另外一个程序垮掉,而使用户无从查找问题的真正根源。此外,即使无害的内存泄漏也可能殃及池鱼。

    幸运的是,Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法。下面请和我一起分享收获——如何使用 CRT 调试功能来检测内存泄漏?

    如何启用内存泄漏检测机制?
    使用 _CrtSetDbgFlag
    设置 CRT 报告模式
    解释内存块类型
    如何在内存分配序号处设置断点?
    如何比较内存状态?
    结论

    如何启用内存泄漏检测机制?

    VC++ IDE 的默认状态是没有启用内存泄漏检测机制的,也就是说即使某段代码有内存泄漏,调试会话的 Output 窗口的 Debug 页不会输出有关内存泄漏信息。你必须设定两个最基本的机关来启用内存泄漏检测机制。

    一是使用调试堆函数:

    #define _CRTDBG_MAP_ALLOC #include<stdlib.h> #include<crtdbg.h>

    注意:#include 语句的顺序。如果更改此顺序,所使用的函数可能无法正确工作。

    通过包含 crtdbg.h 头文件,可以将 malloc 和 free 函数映射到其“调试”版本 _malloc_dbg 和 _free_dbg,这些函数会跟踪内存分配和释放。此映射只在调试(Debug)版本(也就是要定义 _DEBUG)中有效。发行版本(Release)使用普通的 malloc 和 free 函数。

    #define 语句将 CRT 堆函数的基础版本映射到对应的“调试”版本。该语句不是必须的,但如果没有该语句,那么有关内存泄漏的信息会不全。

    二是在需要检测内存泄漏的地方添加下面这条语句来输出内存泄漏信息:

    _CrtDumpMemoryLeaks(); 当在调试器下运行程序时,_CrtDumpMemoryLeaks 将在 Output 窗口的 Debug 页中显示内存泄漏信息。比如:

    Detected memory leaks!Dumping objects ->C:/Temp/memleak/memleak.cpp(15) : {45} normal block at 0x00441BA0, 2 bytes long.Data: <AB> 41 42 c:/program files/microsoft visual studio/vc98/include/crtdbg.h(552) : {44} normal block at 0x00441BD0, 33 bytes long.Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD c:/program files/microsoft visual studio/vc98/include/crtdbg.h(552) : {43} normal block at 0x00441C20, 40 bytes long.Data: < C > 08 02 43 00 16 00 00 00 00 00 00 00 00 00 00 00 Object dump complete.

    如果不使用 #define _CRTDBG_MAP_ALLOC 语句,内存泄漏的输出是这样的:

    Detected memory leaks!Dumping objects ->{45} normal block at 0x00441BA0, 2 bytes long.Data: <AB> 41 42 {44} normal block at 0x00441BD0, 33 bytes long.Data: < C > 00 43 00 CD CD CD CD CD CD CD CD CD CD CD CD CD {43} normal block at 0x00441C20, 40 bytes long.Data: < C > C0 01 43 00 16 00 00 00 00 00 00 00 00 00 00 00 Object dump complete. 根据这段输出信息,你无法知道在哪个源程序文件里发生了内存泄漏。下面我们来研究一下输出信息的格式。第一行和第二行没有什么可说的,从第三行开始:

    xx}:花括弧内的数字是内存分配序号,本文例子中是 {45},{44},{43};block:内存块的类型,常用的有三种:normal(普通)、client(客户端)或 CRT(运行时);本文例子中是:normal block; 用十六进制格式表示的内存位置,如:at 0x00441BA0 等;以字节为单位表示的内存块的大小,如:32 bytes long; 前 16 字节的内容(也是用十六进制格式表示),如:Data: <AB> 41 42 等;

    仔细观察不难发现,如果定义了 _CRTDBG_MAP_ALLOC ,那么在内存分配序号前面还会显示在其中分配泄漏内存的文件名,以及文件名后括号中的数字表示发生泄漏的代码行号,比如:

    C:/Temp/memleak/memleak.cpp(15) 双击 Output 窗口中此文件名所在的输出行,便可跳到源程序文件分配该内存的代码行(也可以选中该行,然后按 F4,效果一样) ,这样一来我们就很容易定位内存泄漏是在哪里发生的了,因此,_CRTDBG_MAP_ALLOC 的作用显而易见。

    使用 _CrtSetDbgFlag

    如果程序只有一个出口,那么调用 _CrtDumpMemoryLeaks 的位置是很容易选择的。但是,如果程序可能会在多个地方退出该怎么办呢?在每一个可能的出口处调用 _CrtDumpMemoryLeaks 肯定是不可取的,那么这时可以在程序开始处包含下面的调用:

    _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

    这条语句无论程序在什么地方退出都会自动调用 _CrtDumpMemoryLeaks。注意:这里必须同时设置两个位域标志:_CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF。

    设置 CRT 报告模式

    默认情况下,_CrtDumpMemoryLeaks 将内存泄漏信息 dump 到 Output 窗口的 Debug 页, 如果你想将这个输出定向到别的地方,可以使用 _CrtSetReportMode 进行重置。如果你使用某个库,它可能将输出定向到另一位置。此时,只要使用以下语句将输出位置设回 Output 窗口即可:

    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

    有关使用 _CrtSetReportMode 的详细信息,请参考 MSDN 库关于 _CrtSetReportMode 的描述。

    解释内存块类型

    前面已经说过,内存泄漏报告中把每一块泄漏的内存分为 normal(普通块)、client(客户端块)和 CRT 块。事实上,需要留心和注意的也就是 normal 和 client,即普通块和客户端块。

    normal block(普通块):这是由你的程序分配的内存。
    client block(客户块):这是一种特殊类型的内存块,专门用于 MFC 程序中需要析构函数的对象。MFC new 操作符视具体情况既可以为所创建的对象建立普通块,也可以为之建立客户块。
    CRT block(CRT 块):是由 C RunTime Library 供自己使用而分配的内存块。由 CRT 库自己来管理这些内存的分配与释放,我们一般不会在内存泄漏报告中发现 CRT 内存泄漏,除非程序发生了严重的错误(例如 CRT 库崩溃)。

    除了上述的类型外,还有下面这两种类型的内存块,它们不会出现在内存泄漏报告中:

    free block(空闲块):已经被释放(free)的内存块。
    Ignore block(忽略块):这是程序员显式声明过不要在内存泄漏报告中出现的内存块。

    如何在内存分配序号处设置断点?

    在内存泄漏报告中,的文件名和行号可告诉分配泄漏的内存的代码位置,但仅仅依赖这些信息来了解完整的泄漏原因是不够的。因为一个程序在运行时,一段分配内存的代码可能会被调用很多次,只要有一次调用后没有释放内存就会导致内存泄漏。为了确定是哪些内存没有被释放,不仅要知道泄漏的内存是在哪里分配的,还要知道泄漏产生的条件。这时内存分配序号就显得特别有用——这个序号就是文件名和行号之后的花括弧里的那个数字。

    例如,在本文例子代码的输出信息中,“45”是内存分配序号,意思是泄漏的内存是你程序中分配的第四十五个内存块:

    Detected memory leaks!Dumping objects ->C:/Temp/memleak/memleak.cpp(15) : {45} normal block at 0x00441BA0, 2 bytes long.Data: <AB> 41 42 ......Object dump complete.

    CRT 库对程序运行期间分配的所有内存块进行计数,包括由 CRT 库自己分配的内存和其它库(如 MFC)分配的内存。因此,分配序号为 N 的对象即为程序中分配的第 N 个对象,但不一定是代码分配的第 N 个对象。(大多数情况下并非如此。)

    这样的话,你便可以利用分配序号在分配内存的位置设置一个断点。方法是在程序起始附近设置一个位置断点。当程序在该点中断时,可以从 QuickWatch(快速监视)对话框或 Watch(监视)窗口设置一个内存分配断点:

    例如,在 Watch 窗口中,在 Name 栏键入下面的表达式:

    _crtBreakAlloc

    如果要使用 CRT 库的多线程 DLL 版本(/MD 选项),那么必须包含上下文操作符,像这样:

    {,,msvcrtd.dll}_crtBreakAlloc

    现在按下回车键,调试器将计算该值并把结果放入 Value 栏。如果没有在内存分配点设置任何断点,该值将为 –1。

    用你想要在其位置中断的内存分配的分配序号替换 Value 栏中的值。例如输入 45。这样就会在分配序号为 45 的地方中断。

    在所感兴趣的内存分配处设置断点后,可以继续调试。这时,运行程序时一定要小心,要保证内存块分配的顺序不会改变。当程序在指定的内存分配处中断时,可以查看 Call Stack(调用堆栈)窗口和其它调试器信息以确定分配内存时的情况。如果必要,可以从该点继续执行程序,以查看对象发生了什么情况,或许可以确定未正确释放对象的原因。

    尽管通常在调试器中设置内存分配断点更方便,但如果愿意,也可在代码中设置这些断点。为了在代码中设置一个内存分配断点,可以增加这样一行(对于第四十五个内存分配):

    _crtBreakAlloc = 45;

    你还可以使用有相同效果的 _CrtSetBreakAlloc 函数:

    _CrtSetBreakAlloc(45);

    如何比较内存状态?

    定位内存泄漏的另一个方法就是在关键点获取应用程序内存状态的快照。CRT 库提供了一个结构类型 _CrtMemState。你可以用它来存储内存状态的快照:

    _CrtMemState s1, s2, s3;

    若要获取给定点的内存状态快照,可以向 _CrtMemCheckpoint 函数传递一个 _CrtMemState 结构。该函数用当前内存状态的快照填充此结构:

    _CrtMemCheckpoint( &s1 );

    通过向 _CrtMemDumpStatistics 函数传递 _CrtMemState 结构,可以在任意地方 dump 该结构的内容:

    _CrtMemDumpStatistics( &s1 );

    该函数输出如下格式的 dump 内存分配信息:

    0 bytes in 0 Free Blocks.75 bytes in 3 Normal Blocks.5037 bytes in 41 CRT Blocks.0 bytes in 0 Ignore Blocks.0 bytes in 0 Client Blocks.Largest number used: 5308 bytes.Total allocations: 7559 bytes.

    若要确定某段代码中是否发生了内存泄漏,可以通过获取该段代码之前和之后的内存状态快照,然后使用 _CrtMemDifference 比较这两个状态:

    _CrtMemCheckpoint( &s1 );// 获取第一个内存状态快照// 在这里进行内存分配_CrtMemCheckpoint( &s2 );// 获取第二个内存状态快照// 比较两个内存快照的差异if ( _CrtMemDifference( &s3, &s1, &s2) ) _CrtMemDumpStatistics( &s3 );// dump 差异结果

    顾名思义,_CrtMemDifference 比较两个内存状态(前两个参数),生成这两个状态之间差异的结果(第三个参数)。在程序的开始和结尾放置 _CrtMemCheckpoint 调用,并使用 _CrtMemDifference 比较结果,是检查内存泄漏的另一种方法。如果检测到泄漏,则可以使用 _CrtMemCheckpoint 调用通过二进制搜索技术来分割程序和定位泄漏。

    结论

    尽管 VC ++ 具有一套专门调试 MFC 应用程序的机制,但本文上述讨论的内存分配很简单,没有涉及到 MFC 对象,所以这些内容同样也适用于 MFC 程序。在 MSDN 库中可以找到很多有关 VC++ 调试方面的资料,如果你能善用 MSDN 库,相信用不了多少时间你就有可能成为调试高

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/jnstone3/archive/2009/06/13/4266516.aspx

    展开全文
  • vc++内存泄漏检测

    千次阅读 2007-11-09 21:26:00
    7月18日vc++内存泄漏检测VC++6.0中内存泄漏检测 http://blog.vckbase.com/bruceteen/archive/2004/10/28/1130.html最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看...
  • vc 内存泄漏检测方法

    2015-06-17 11:24:57
    最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他指点一...
  • 有小伙伴说到,系统是win10,之前电脑的内存是4G,某天凑巧打开了任务管理器,,一,吓了一大跳!内存占用竟高达50%。...鲁大师检测结果8G内存,但系统却显示只有4G,那么,这50%的占用或只剩4G...
  • CRT检测内存泄漏

    2006-06-10 14:55:00
    CRT调试功能来检测内存泄漏载例子源代码http://www.vckbase.com/code/envdebug/debug/myleaktest.rar最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口...
  • VS下内存泄露的检测

    2014-02-26 14:12:17
    原文地址:VS下内存泄露的检测作者:暂且么幺 ... 最近了周星星 Blog 中的一篇文章:“VC++6.0...窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他指点一、二,然而未果
  • VC++6.0中内存泄漏检测

    千次阅读 2012-10-19 11:50:48
    最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他指点一...
  • VC++ 6.0 中如何使用 ... 最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找
  •  最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他指点...
  •  最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他...
  •  最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他指点...
  • CRT 调试功能来检测内存泄漏作者:JerryZ下载例子源代码 最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期...
  • 最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜怎么弄其输出都不是预期的东西,郁闷了半天,便到水坛里找到周星星,请求他指点一...
  • 内存泄露调试

    千次阅读 2010-07-12 17:27:00
    http://blog.vckbase.com/bruceteen/archive/2004/10/28/1130.html<br />最近了周星星 Blog 中的一篇文章:“VC++6.0中内存泄漏检测”,受益匪浅,便运行其例子代码想看看 Output 窗口中的输出结果,可惜...
  • 可以使用MemTest 这个软件来检测一下内存,它可以彻底的检测内存的稳定度。 假如是双内存,而且是不同品牌的内存条混插或者买了二手内存时,出现这个问题,这时,就要检查是不 是内存出问题了或者和其它硬件不兼容...
  • 参数四,阈值,大于阈值threshold的线段才可以被检测通过并返回到结果中 #该函数返回值为rho与theta lines = cv.HoughLines(edges, 1, np.pi/180, 200) for line in lines: rho, theta = line[0] #line[0]...

空空如也

空空如也

1 2 3
收藏数 59
精华内容 23
关键字:

内存检测结果怎么看