精华内容
下载资源
问答
  • 在80386以上档次的微机中配置了高速缓冲存储器(Cache),这时内存包括主存与高速缓存两部分。 计算器内存条采用的是DRAM(动态随机存储器),即计算机的主存。通常所说的内存容量即指内存条DRAM的大小。 高速缓冲...

    在80386以上档次的微机中配置了高速缓冲存储器(Cache),这时内存包括主存与高速缓存两部分。


    计算器内存条采用的是DRAM(动态随机存储器),即计算机的主存。通常所说的内存容量即指内存条DRAM的大小。


    高速缓冲存储器Cache主要是为了解决CPU和主存速度不匹配而设计的。Cache一般由SRAM(静态随机存储器)芯片实现,它的存取速度接近CPU,快于DRAM,存储容量小于DRAM。它比主存的优先级高,CPU存取信息时优先访问Cache,找不到的话再去主存DRAM中找,同时把信息周围的数据块从主存复制到Cache中。


    现代计算机系统基本都采用Cache-主存-辅存(即外存储器)三级存储系统。其中CPU可直接访问Cache和主存,辅存则通过主存与CPU交换信息。


          严格的说,内存包括主存(内存条,基于DRAM)与高速缓存(Cache,基于SRAM)两部分。可能是由于Cache相较内存条容量很小,毕竟内存容量只计内存条大小,加上重要性也不及内存条,一般人或许不知道Cache,所以就忽略了高速缓存Cache,直接将主存--内存条等同了内存吧。

     

     

     

     

     

     

     

    展开全文
  • 什么内存

    2013-11-18 16:17:55
    在计算机的组成结构中,有一个很重要的部分,就是存储器。存储器是用来存储程序数据的...内存一般采用半导体存储单元,包括随机存储器(RAM),只读存储器(ROM),以及高速缓存(CACHE)。只不过因为RAM是其中最重要
    在计算机的组成结构中,有一个很重要的部分,就是存储器。存储器是用来存储程序和数据的部件,对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作。存储器的种类很多,按其用途可分为主存储器和辅助存储器,主存储器又称内存储器(简称内存).内存在电脑中起着举足轻重的作用。内存一般采用半导体存储单元,包括随机存储器(RAM),只读存储器(ROM),以及高速缓存(CACHE)。只不过因为RAM是其中最重要的存储器。S(SYSNECRONOUS)DRAM 同步动态随机存取存储器:SDRAM为168脚,这是目前PENTIUM及以上机型使用的内存。SDRAM将系统总线与RAM通过一个相同的时钟锁在一起,使系统总线和RAM能够共享一个时钟周期,以相同的速度同步工作,每一个时钟脉冲的上升沿便开始传递数据,速度比EDO内存提高50%。DDR(DOUBLE DATA RAGE)RAM :SDRAM的更新换代产品,他允许在时钟脉冲的上升沿和下降沿传输数据,这样不需要提高时钟的频率就能加倍提高SDRAM的速度。
    ●内存
    内存就是存储程序以及数据的地方,比如当我们在使用WPS处理文稿时,当你在键盘上敲入字符时,它就被存入内存中,当你选择存盘时,内存中的数据才会被存入硬(磁)盘。在进一步理解它之前,还应认识一下它的物理概念。
    ●只读存储器(ROM)
    ROM表示只读存储器(Read Only Memory),在制造ROM的时候,信息(数据或程序)就被存入并永久保存。这些信息只能读出,一般不能写入,即使机器掉电,这些数据也不会丢失。ROM一般用于存放计算机的基本程序和数据,如BIOS ROM。其物理外形一般是双列直插式(DIP)的集成块。
    ●随机存储器(RAM)
    随机存储器(Random Access Memory)表示既可以从中读取数据,也可以写入数据。当机器电源关闭时,存于其中的数据就会丢失。我们通常购买或升级的内存条就是用作电脑的内存,内存条(SIMM)就是将RAM集成块集中在一起的一小块电路板,它插在计算机中的内存插槽上,以减少RAM集成块占用的空间。目前市场上常见的内存条有128M/条、256M/条、512M/条等。
    ●高速缓冲存储器(Cache)
    Cache也是我们经常遇到的概念,它位于CPU与内存之间,是一个读写速度比内存更快的存储器。当CPU向内存中写入或读出数据时,这个数据也被存储进高速缓冲存储器中。当CPU再次需要这些数据时,CPU就从高速缓冲存储器读取数据,而不是访问较慢的内存,当然,如需要的数据在Cache中没有,CPU会再去读取内存中的数据。
    当你理解了上述概念后,也许你会问,内存就是内存,为什么又会出现各种内存名词,这到底又是怎么回事呢?
    在回答这个问题之前,我们再来看看下面这一段。
    物理存储器和地址空间
    物理存储器和存储地址空间是两个不同的概念。但是由于这两者有十分密切的关系,而且两者都用B、KB、MB、GB来度量其容量大小,因此容易产生认识上的混淆。初学者弄清这两个不同的概念,有助于进一步认识内存储器和用好内存储器。
    物理存储器是指实际存在的具体存储器芯片。如主板上装插的内存条和装载有系统的BIOS的ROM芯片,显示卡上的显示RAM芯片和装载显示BIOS的ROM芯片,以及各种适配卡上的RAM芯片和ROM芯片都是物理存储器。
    存储地址空间是指对存储器编码(编码地址)的范围。所谓编码就是对每一个物理存储单元(一个字节)分配一个号码,通常叫作“编址”。分配一个号码给一个存储单元的目的是为了便于找到它,完成数据的读写,这就是所谓的“寻址”(所以,有人也把地址空间称为寻址空间)。
    地址空间的大小和物理存储器的大小并不一定相等。举个例子来说明这个问题:某层楼共有17个房间,其编号为801~817。这17个房间是物理的,而其地址空间采用了三位编码,其范围是800~899共100个地址,可见地址空间是大于实际房间数量的。
    对于386以上档次的微机,其地址总线为32位,因此地址空间可达232即4GB。但实际上我们所配置的物理存储器通常只有1MB、2MB、4MB、8MB、16MB、32MB等,远小于地址空间所允许的范围。
    好了,现在可以解释为什么会产生诸如:常规内存、保留内存、上位内存、高端内存、扩充内存和扩展内存等不同内存类型。
    各种内存概念
    这里需要明确的是,我们讨论的不同内存的概念是建立在寻址空间上的。
    IBM推出的第一台PC机采用的CPU是8088芯片,它只有20根地址线,也就是说,它的地址空间是1MB。
    PC机的设计师将1MB中的低端640KB用作RAM,供DOS及应用程序使用,高端的384KB则保留给ROM、视频适配卡等系统使用。从此,这个界限便被确定了下来并且沿用至今。低端的640KB就被称为常规内存即PC机的基本RAM区。保留内存中的低128KB是显示缓冲区,高64KB是系统BIOS(基本输入/输出系统)空间,其余192KB空间留用。从对应的物理存储器来看,基本内存区只使用了512KB芯片,占用0000至80000这512KB地址。显示内存区虽有128KB空间,但对单色显示器(MDA卡)只需4KB就足够了,因此只安装4KB的物理存储器芯片,占用了B0000至B10000这4KB的空间,如果使用彩色显示器(CGA卡)需要安装16KB的物理存储器,占用B8000至BC000这16KB的空间,可见实际使用的地址范围都小于允许使用的地址空间。
    在当时(1980年末至1981年初)这么“大”容量的内存对PC机使用者来说似乎已经足够了,但是随着程序的不断增大,图象和声音的不断丰富,以及能访问更大内存空间的新型CPU相继出现,最初的PC机和MS-DOS设计的局限性变得越来越明显。
    1.什么是扩充内存?
    EMS工作原理
    到1984年,即286被普遍接受不久,人们越来越认识到640KB的限制已成为大型程序的障碍,这时,Intel和Lotus,这两家硬、软件的杰出代表,联手制定了一个由硬件和软件相结合的方案,此方法使所有PC机存取640KB以上RAM成为可能。而Microsoft刚推出Windows不久,对内存空间的要求也很高,因此它也及时加入了该行列。
    在1985年初,Lotus、Intel和Microsoft三家共同定义了LIM-EMS,即扩充内存规范,通常称EMS为扩充内存。当时,EMS需要一个安装在I/O槽口的内存扩充卡和一个称为EMS的扩充内存管理程序方可使用。但是I/O插槽的地址线只有24位(ISA总线),这对于386以上档次的32位机是不能适应的。所以,现在已很少使用内存扩充卡。现在微机中的扩充内存通常是用软件如DOS中的EMM386把扩展内存模拟或扩充内存来使用。所以,扩充内存和扩展内存的区别并不在于其物理存储器的位置,而在于使用什么方法来读写它。下面将作进一步介绍。
    前面已经说过扩充存储器也可以由扩展存储器模拟转换而成。EMS的原理和XMS不同,它采用了页帧方式。页帧是在1MB空间中指定一块64KB空间(通常在保留内存区内,但其物理存储器来自扩展存储器),分为4页,每页16KB。EMS存储器也按16KB分页,每次可交换4页内容,以此方式可访问全部EMS存储器。符合EMS的驱动程序很多,常用的有EMM386.EXE、QEMM、TurboEMS、386MAX等。DOS和Windows中都提供了EMM386.EXE。
    2.什么是扩展内存?
    我们知道,286有24位地址线,它可寻址16MB的地址空间,而386有32位地址线,它可寻址高达4GB的地址空间,为了区别起见,我们把1MB以上的地址空间称为扩展内存XMS(eXtend memory)。
    在386以上档次的微机中,有两种存储器工作方式,一种称为实地址方式或实方式,另一种称为保护方式。在实方式下,物理地址仍使用20位,所以最大寻址空间为1MB,以便与8086兼容。保护方式采用32位物理地址,寻址范围可达4GB。DOS系统在实方式下工作,它管理的内存空间仍为1MB,因此它不能直接使用扩展存储器。为此,Lotus、Intel、AST及Microsoft公司建立了MS-DOS下扩展内存的使用标准,即扩展内存规范XMS。我们常在Config.sys文件中看到的Himem.sys就是管理扩展内存的驱动程序。
    扩展内存管理规范的出现迟于扩充内存管理规范。
    3.什么是高端内存区?
    在实方式下,内存单元的地址可记为:
    段地址:段内偏移
    通常用十六进制写为XXXX:XXXX。实际的物理地址由段地址左移4位再和段内偏移相加而成。若地址各位均为1时,即为FFFF:FFFF。其实际物理地址为:FFF0+FFFF=10FFEF,约为1088KB(少16字节),这已超过1MB范围进入扩展内存了。这个进入扩展内存的区域约为64KB,是1MB以上空间的第一个64KB。我们把它称为高端内存区HMA(High Memory Area)。HMA的物理存储器是由扩展存储器取得的。因此要使用HMA,必须要有物理的扩展存储器存在。此外HMA的建立和使用还需要XMS驱动程序HIMEM.SYS的支持,因此只有装入了HIMEM.SYS之后才能使用HMA。
    4.什么是上位内存?
    为了解释上位内存的概念,我们还得回过头看看保留内存区。保留内存区是指640KB~1024KB(共384KB)区域。这部分区域在PC诞生之初就明确是保留给系统使用的,用户程序无法插足。但这部分空间并没有充分使用,因此大家都想对剩余的部分打主意,分一块地址空间(注意:是地址空间,而不是物理存储器)来使用。于是就得到了又一块内存区域UMB。
    UMB(Upper Memory Blocks)称为上位内存或上位内存块。它是由挤占保留内存中剩余未用的空间而产生的,它的物理存储器仍然取自物理的扩展存储器,它的管理驱动程序是EMS驱动程序。
    5.什么是SHADOW(影子)内存?
    对于细心的读者,可能还会发现一个问题:即是对于装有1MB或1MB以上物理存储器的机器,其640KB~1024KB这部分物理存储器如何使用的问题。由于这部分地址空间已分配为系统使用,所以不能再重复使用。为了利用这部分物理存储器,在某些386系统中,提供了一个重定位功能,即把这部分物理存储器的地址重定位为1024KB~1408KB。这样,这部分物理存储器就变成了扩展存储器,当然可以使用了。但这种重定位功能在当今高档机器中不再使用,而把这部分物理存储器保留作为Shadow存储器。Shadow存储器可以占据的地址空间与对应的ROM是相同的。Shadow由RAM组成,其速度大大高于ROM。当把ROM中的内容(各种BIOS程序)装入相同地址的Shadow RAM中,就可以从RAM中访问BIOS,而不必再访问ROM。这样将大大提高系统性能。因此在设置CMOS参数时,应将相应的Shadow区设为允许使用(Enabled)。
    6、什么是奇/偶校验?
    奇/偶校验(ECC)是数据传送时采用的一种校正数据错误的一种方式,分为奇校验和偶校验两种。
    如果是采用奇校验,在传送每一个字节的时候另外附加一位作为校验位,当实际数据中“1”的个数为偶数的时候,这个校验位就是“1”,否则这个校验位就是“0”,这样就可以保证传送数据满足奇校验的要求。在接收方收到数据时,将按照奇校验的要求检测数据中“1”的个数,如果是奇数,表示传送正确,否则表示传送错误。
    同理偶校验的过程和奇校验的过程一样,只是检测数据中“1”的个数为偶数。
    总 结
    经过上面分析,内存储器的划分可归纳如下:
    ●基本内存 占据0~640KB地址空间。
    ●保留内存 占据640KB~1024KB地址空间。分配给显示缓冲存储器、各适配卡上的ROM和系统ROM BIOS,剩余空间可作上位内存UMB。UMB的物理存储器取自物理扩展存储器。此范围的物理RAM可作为Shadow RAM使用。
    ●上位内存(UMB)利用保留内存中未分配使用的地址空间建立,其物理存储器由物理扩展存储器取得。UMB由EMS管理,其大小可由EMS驱动程序设定。
    ●高端内存(HMA) 扩展内存中的第一个64KB区域(1024KB~1088KB)。由HIMEM.SYS建立和管理。
    ●XMS内存 符合XMS规范管理的扩展内存区。其驱动程序为HIMEM.SYS。
    ●EMS内存 符合EMS规范管理的扩充内存区。其驱动程序为EMM386.EXE等。
    展开全文
  • Java内存分配主要包括以下几个区域: 1. 寄存器:我们在程序中无法控制 2. 栈:存放基本类型的数据对象的引用,但对象本身不存放在栈中,而是存放在堆中 3. 堆:存放用new产生的数据 4. 静态域:存放在对象中用...

    Java内存分配主要包括以下几个区域:

    1. 寄存器:我们在程序中无法控制

    2. 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中

    3. 堆:存放用new产生的数据

    4. 静态域:存放在对象中用static定义的静态成员

    5. 常量池:存放常量

    6. 非RAM(随机存取存储器)存储:硬盘等永久存储空间

    *****************************************************************

    Java内存分配中的栈

      在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 

    Java内存分配中的堆

      堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

       在堆中产生了一个数组或对象后,还可以 在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。

      引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍 然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。 

      实际上,栈中的变量指向堆内存中的变量,这就是Java中的指针! 

    常量池 (constant pool)

      常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final)还包含一些以文本形式出现的符号引用,比如: 

    1. 类和接口的全限定名;
    2. 字段的名称和描述符; 
    3. 方法和名称和描述符。

      虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和 floating point常量)和对其他类型,字段和方法的符号引用。

      对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的, 对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引 用。说到这里,对常量池中的字符串值的存储位置应该有一个比较明了的理解了。

      在程序执行的时候,常量池会储存在Method Area,而不是堆中。

    堆与栈

      Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、 anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态 分配内存,存取速度较慢。 

      栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量数据(int, short, long, byte, float, double, boolean, char)和对象句柄(引用)。

    ******************************************************************

     

      这里我们主要关心栈,堆和常量池,对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。

    字符串内存分配:

      对于字符串,其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。 

    如以下代码:

            String s1 = "china";
            String s2 = "china";
            String s3 = "china";
    
            String ss1 = new String("china");
            String ss2 = new String("china");
            String ss3 = new String("china");

      这里解释一下黄色这3个箭头,对于通过new产生一个字符串(假设为“china”)时,会先去常量池中查找是否已经有了“china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。

      这也就是有道面试题:Strings=newString(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有”xyz”,就是两个。

      存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的 intern()方法就是扩充常量池的 一个方法;当一个String实例str调用intern()方法时,Java 查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用

    如下代码:

            String s0= "kvill";   
            String s1=new String("kvill");   
            String s2=new String("kvill");   
            System.out.println( s0==s1 );     
            s1.intern();   
            s2=s2.intern(); //把常量池中"kvill"的引用赋给s2   
            System.out.println( s0==s1);   
            System.out.println( s0==s1.intern() );   
            System.out.println( s0==s2 ); 

    输出结果:

    false
    false
    true
    true

    String常量池问题的几个例子:

    【1】
    String a = "ab";   
    String bb = "b";   
    String b = "a" + bb;   
    System.out.println((a == b)); //result = false 
    
    【2】
    String a = "ab";   
    final String bb = "b";   
    String b = "a" + bb;   
    System.out.println((a == b)); //result = true 
    
    【3】
    String a = "ab";   
    final String bb = getBB();   
    String b = "a" + bb;   
    System.out.println((a == b)); //result = false   
    private static String getBB() {  
    return "b";   
    } 

    分析:

      【1】中,JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a" + bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b。所以上面程序的结果也就为false。

      【2】和【1】中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。故上面程序的结果为true。

      【3】JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为b,故上面程序的结果为false。

    结论:

      字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定(常量池和堆).有的是编译期就已经创建好,存放在字符串常 量池中,而有的是运行时才被创建.使用new关键字,存放在堆中。

    基础类型的变量和常量在内存中的分配

      对于基础类型的变量和常量,变量和引用存储在栈中,常量存储在常量池中。

    如以下代码:

            int i1 = 9;
            int i2 = 9;
            int i3 = 9;
    
            final int INT1 = 9;
            final int INT2 = 9;
            final int INT3 = 9;

      编译器先处理int i1 = 9;首先它会在栈中创建一个变量为i1的引用,然后查找栈中是否有9这个值,如果没找到,就将9存放进来,然后将i1指向9。接着处理int i2 = 9;在创建完i2的引用变量后,因为在栈中已经有9这个值,便将i2直接指向9。这样,就出现了i1与i2同时均指向9的情况。最后i3也指向这个9。

    成员变量和局部变量在内存中的分配

      对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。 形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。 成员变量存储在堆中的对象里面,由垃圾回收器负责回收。   如以下代码:

    class BirthDate {
        private int day;
        private int month;
        private int year;
    
        public BirthDate(int d, int m, int y) {
            day = d;
            month = m;
            year = y;
        }
        // 省略get,set方法………
    }
    
    public class Test {
        public static void main(String args[]) {
            int date = 9;
            Test test = new Test();
            test.change(date);
            BirthDate d1 = new BirthDate(7, 7, 1970);
        }
    
        public void change(int i) {
            i = 1234;
        }
    }

      对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化:    

    1. main方法开始执行:int date = 9; date局部变量,基础类型,引用和值都存在栈中。
    2. Test test = new Test();test为对象引用,存在栈中,对象(new Test())存在堆中。 
    3. test.change(date);  i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。
    4. BirthDate d1= new BirthDate(7,7,1970); d1为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。 
    5. main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(), new BirthDate()将等待垃圾回收。
    展开全文
  • 什么是内存在Android中,通常我们说的内存就是指RAM(随机存取存储器),可以读出读写或者改写,它包括寄存器,栈,堆,静态存储区域,常量池。而且当断电的时候,内存里的东西就销毁了不会保留下来。垃圾回收我们...

    什么是内存

    在Android中,通常我们说的内存就是指RAM(随机存取存储器),可以读出读写或者改写,它包括寄存器,栈,堆,静态存储区域,常量池。而且当断电的时候,内存里的东西就销毁了不会保留下来。

    垃圾回收

    我们知道,java本身就有垃圾回收机制,当我们通过new出一个对象的时候,java虚拟机会为对象分配内存,内存的释放是由垃圾收集器(GC)来回收的,这样做的好处就是可以让开发者专注于功能和业务逻辑的开发,也更加的高效。然而这样也有一些弊端比如会占用大量的资源,由于人为不可控,所以难免会存在部分对象忘记垃圾回收的现象发生,这就是造成内存泄漏的原因。首先说一下java虚拟机的垃圾回收(GC)。

    这里写图片描述

    他把对象分为Young Generation(年轻代)、Old Generation(老年代)、Permanent Generation(持久代)

    • Young Generation:年轻代又被分为三个区,一个eden区,两个Survivor区,一般程序中生成的大部分对象都在Eden区,当Eden区满了之后,会执行垃圾回收,把还存活的对象转移到第一个Survivor区(S0)。同时如果S0满了之后,垃圾回收也检查在S0存活下来的对象,并转移到第二个Survivor区(S1)
    • Old Generation:存放的是长期存活的对象,或者经过多次GC之后依然活下来的对象
    • Permanent Generation:用于存放静态的类和方法,常量,属性和方法信息

    虽然有很多不同的垃圾回收的策略,但通常我们不去关心垃圾回收所用的时间,因为一般垃圾回收所用的时间都是很短的,而是关心其产生的不好的影响,比如Android系统每隔16ms会发出一个触发对UI进行渲染,当帧率大于60FPS,或者这个渲染过程如果保证在16ms以内就能达到一个流畅的画面,然而如果UI渲染过程发生GC,导致绘制时间超过16ms,,就会让用户发送卡顿,应用在手机上的寿命就不长了。。。

    内存泄漏

    上面已经提到,本来可以回收了然而没有及时回收就会导致内存泄漏的问题,一般有以下5种情况:

    • 单例模式:
    public class AppManager {
    
        private static AppManager instance;
    
        private Context context;
    
        private AppManager(Context context) {
    
            this.context = context;
        }
    
        public static AppManager getInstance(Context context) {
    
            if (instance != null) {
    
                instance = new AppManager(context);
    
            }
    
            return instance;
        }
    }

    一般上面都会传入Activity的上下文,然而单例的周期应该是和应用的一样,所以如果Activity销毁的时候,由于单例持有Activity的引用,所以Activity的内存不会被回收,这就导致了内存泄漏,所以可以把Context改成Application的上下文,这样Application结束的时候,单例也销毁了:

    
        private AppManager(Context context) {
    
            this.context = context.getApplicationContext();
    
        }
    • 非静态内部类的静态实例造成的泄漏
    public class MainActivity extends AppCompatActivity {
    
        private static TestResource mResource = null;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
    
            if(mManager == null){
    
                mManager = new TestResource();
    
            }
    
            //...
        }
    
        class TestResource {
    
            //...
    
        }
    }

    一般来说非静态内部类会默认持有外部类的引用,而这个内部类又创建了一个一个静态实例,导致这个实例和应用的周期一样长,所以当Activity销毁的时候又不能释放内存,所以也会造成内存泄漏。修改的方法就是将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请使用Application的Context 。

    • 还有一个比较常见的就是Handler引起的内存泄漏
    public class MainActivity extends AppCompatActivity {
    
        private Handler mHandler = new Handler() {
    
            @Override
            public void handleMessage(Message msg) {
    
                //...
    
            }
    
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
    
            loadData();
    
        }
    
        private void loadData(){
    
            //...request
    
            Message message = Message.obtain();
    
            mHandler.sendMessage(message);
    
        }
    
    }

    mHandler作为Handler的非静态内部类的实例,默认持有外部类Activity的引用,消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。解决方法就是使用静态类并实现Hanlder对Activity的弱引用:

        private static class MyHandler extends Handler {
    
            private WeakReference<Context> reference;
    
            public MyHandler(Context context) {
    
                reference = new WeakReference<>(context);
            }
    
            @Override
            public void handleMessage(Message msg) {
                //...
            }
        }
    • 线程造成的内存泄漏,基本我们习惯于这样写:
    
            new Thread(new Runnable() {
    
                @Override
    
                public void run() {
    
                    SystemClock.sleep(10000);
    
                }
    
            }).start();

    原因和第二个差不多,Activity销毁的时候,线程可能还没有结束,此时Activity便不能被回收。解决方法,改成静态内部类。

    • 资源未关闭造成的内存泄漏
      比如使用的Cursor,BraodcastReceiver等在Activity销毁的时候没有及时关闭或者注销,也会造成内存泄漏的问题。

    以上就是平时会遇到的内存泄漏的问题。

    内存检测工具

    android studio 为我们提供了一些检测内存的工具,具体使用见另外一篇文章。

    展开全文
  • 问题思考 ...内存什么内存是ram(随机存储器,Random access memory),是可以直接与cpu叫唤数据的内部寄存器。现在大部分都使用ddr来作为内存,ddr包括DDR3L、DDR4L、LPDDR3/4。ddr的初始化一般是在B
  • 内存包括RAM(主存随机存储器)、ROM(只读存储器,一次读入,反复使用)。以及CACHE(高速缓存)。 ROM(只读存储器):在制造ROM的时候,信息(数据或程序)就被存入并永久保存。这些信息只能读出,一般不能写入,...
  • 内存一般采用半导体存储单元,包括随机存储器(RAM),只读存储器(ROM),以及高速缓存(CACHE)。只不过因为RAM是其中最重要的存储器,所以通常所说的内存即指电脑系统中的RAM。 内存与储存的差别:大多数人常...
  • 主板上承载着CPU(即中央处理器)、内存随机存取存储器为扩展卡提供的插槽 (可是CPU和内存并不是集成在主板上,不是主板的附件,本身也属于电脑硬件) 主板,又叫主机板(mainboard)、系统板(systemboard)或...
  • Java基础知识之字符串

    2017-07-25 18:00:23
    通常我们所说的内存就是暂时存储程序以及数据的地方,包括随机存储器(RAM),只读存储器(ROM),以及高速缓存(CACHE)。只不过因为RAM是其中最重要的存储器。 我们来看RAM组成: 寄存器:速度最快的存储场所,...
  • 数据结构——链表

    2019-01-25 20:37:45
    节点包括两个字段,即存储在该地址的数据包含下一节点地址的指针。链表的最后一个节点包含指向null的指针。 1.链表的用途 链表不需要连续存在于存储器中。节点可以是存储器中任何位置并链接在一起以形成链表。...
  • 会计理论考试题

    2012-03-07 21:04:40
    A、可以减少屏幕损耗 B、可以节省计算机内存 C、可以保障系统安全 D、可以增加动感 36.记录在存储介质上的一组相关信息的集合称为 __D_ 。 A、程序 B、磁盘 C、软件 D、文件 37.在资源管理器中,双击扩展名为“.TXT...
  • 电脑蓝屏对照码

    2019-05-05 14:16:40
    ◆错误分析:系统内存存储器奇偶校验产生错误, 通常是因为有缺陷的内存(包括物理内存、二级缓存或者显卡显存)时设备驱动程序访问不存在的内存地址等原因引起的. 另外, 硬盘被病毒或者其他问题所损伤, 以出现这个停机...
  • WIN XP蓝屏代码大全

    2013-08-08 12:29:21
    ◆错误分析:系统内存存储器奇偶校验产生错误, 通常是因为有缺陷的内存(包括物理内存、二级缓存或者显卡显存)时设备驱动程序访问不存在的内存地址等原因引起的. 另外, 硬盘被病毒或者其他问题所损伤, 以出现这个停机...
  • 功能主要有内存的分配管理、内存的扩充技术、内存保护技术 2、分区分配存储管理 分为固定分区、可变分区、可重定位分区、多重分区。 内存“扩充”技术: •交换:由操作系统做,用户不知道。 ...
  • 引用类型原始类型具有不同的特征用法,它们包括:大小速度问题,这种类型以哪种类型的数据结构存储,当引用类型原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始...
  •  虽然你已知道了如何新增删除一些随机启动程序,但你又知不知道,在开机至到进入Windows的那段时间,计算机在做着什么呢?又或者是,执行着什么程序呢?那些程序,必定要全部载完才开始进入Windows,你有否想过,...
  • java 面试题 总结

    2009-09-16 08:45:34
    引用类型原始类型具有不同的特征用法,它们包括:大小速度问题,这种类型以哪种类型的数据结构存储,当引用类型原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始...
  • 第一遍第二遍扫描中所使用的完整内存结构集合如图6-3所示。 需要注意的另外一点是,上述非频繁项去除的好处会被放大:如果只有一半的项是频繁项,那么在计数过程中仅需要原来空间的1/4。类似地,如果使用三元组...
  • 第一遍第二遍扫描中所使用的完整内存结构集合如图6-3所示。 需要注意的另外一点是,上述非频繁项去除的好处会被放大:如果只有一半的项是频繁项,那么在计数过程中仅需要原来空间的1/4。类似地,如果使用三元组...
  • 包括测量初始化模块、显示模块、信号频率测量模块、量程自动转换模 块、信号周期测量模块、定时器中断服务模块、浮点数格式化模块、浮点数算 术运算模块、浮点数到BCD 码转换模块。 由于数据处理、脉冲计数/定时、...
  • 内存模块上的EEPROM (只读存储器) 储存有关模块的重要参数信息,例如内存类型、大小、速度、电压接口及模块储存区域。 CAS Latency Time: 这个项目可控制DRAM读取指令与数据成为真正可用的时间之间的延迟时间。...
  • 数据结构(C++)有关练习题

    热门讨论 2008-01-02 11:27:18
    <br> 实验八 综合实验 内容及步骤: 1、请使用C++编写班级学生学籍管理程序 每个学生的信息包括:姓名、学号英语、数学、程序设计及体育成绩。从键盘输入数据,建立数据文件student.dat,然后,...

空空如也

空空如也

1 2
收藏数 25
精华内容 10
关键字:

内存包括随机存储器和什么