内存 订阅
内存是计算机中重要的部件之一,它是外存与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。内存(Memory)也被称为内存储器和主存储器,其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,操作系统就会把需要运算的数据从内存调到CPU中进行运算,当运算完成后CPU再将结果传送出来,内存的运行也决定了计算机的稳定运行。 内存条是由内存芯片、电路板、金手指等部分组成的。 [1] 展开全文
内存是计算机中重要的部件之一,它是外存与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。内存(Memory)也被称为内存储器和主存储器,其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,操作系统就会把需要运算的数据从内存调到CPU中进行运算,当运算完成后CPU再将结果传送出来,内存的运行也决定了计算机的稳定运行。 内存条是由内存芯片、电路板、金手指等部分组成的。 [1]
信息
接口类型
DIP、SIMM、DIMM
技术指标
内存容量、存取时间、延迟
别    名
内存储器
中文名
内存
所    属
计算机
外文名
Memory
内存概述
在计算机的组成结构中,有一个很重要的部分,就是存储器。存储器是用来存储程序和数据的部件,对于计算机来说,有了存储器,才有记忆功能,才能保证正常工作。存储器的种类很多,按其用途可分为主存储器和辅助存储器,主存储器又称内存储器(简称内存,港台称之为记忆体)。 [2]  内存又称主存,是CPU能直接寻址的存储空间,由半导体器件制成。内存的特点是存取速率快。内存是电脑中的主要部件,它是相对于外存而言的。我们平常使用的程序,如Windows操作系统、打字软件、游戏软件等,一般都是安装在硬盘等外存上的,但仅此是不能使用其功能的,必须把它们调入内存中运行,才能真正使用其功能,我们平时输入一段文字,或玩一个游戏,其实都是在内存中进行的。就好比在一个书房里,存放书籍的书架和书柜相当于电脑的外存,而我们工作的办公桌就是内存。通常我们把要永久保存的、大量的数据存储在外存上,而把一些临时的或少量的数据和程序放在内存上,当然内存的好坏会直接影响电脑的运行速度。 [2]  内存就是暂时存储程序以及数据的地方,当我们在使用WPS处理文稿时,当你在键盘上敲入字符时,它就被存入内存中,当你选择存盘时,内存中的数据才会被存入硬(磁)盘。 [2] 
收起全文
精华内容
下载资源
问答
  • H2 Database(H2内存数据库)

    千次下载 热门讨论 2013-08-15 23:55:53
    H2就不做很多介绍了。资源包内容列表是我进行H2预研是收集的H2资料,应该是最全面的的了: ...10、H2内存数据库h2部署操作手册.docx 11、H2内存数据库安装与维护.doc 12、H2数据库基础知识.docx 13、H2数据库使用.doc
  • 全面理解Java内存模型(JMM)及volatile关键字

    万次阅读 多人点赞 2017-06-12 11:25:05
    对于硬件内存来说只有寄存器、缓存内存、主内存的概念,并没有工作内存(线程私有数据区域)和主内存(堆内存)之分,也就是说Java内存模型对内存的划分对硬件内存并没有任何影响,因为JMM只是一种抽象的概念,是一组...

    【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
    http://blog.csdn.net/javazejian/article/details/72772461
    出自【zejian的博客】

    关联文章:

    深入理解Java类型信息(Class对象)与反射机制

    深入理解Java枚举类型(enum)

    深入理解Java注解类型(@Annotation)

    深入理解Java类加载器(ClassLoader)

    深入理解Java并发之synchronized实现原理

    Java并发编程-无锁CAS与Unsafe类及其并发包Atomic

    深入理解Java内存模型(JMM)及volatile关键字

    剖析基于并发AQS的重入锁(ReetrantLock)及其Condition实现原理

    剖析基于并发AQS的共享锁的实现(基于信号量Semaphore)

    并发之阻塞队列LinkedBlockingQueue与ArrayBlockingQueue

    本篇主要结合博主个人对Java内存模型的理解以及相关书籍内容的分析作为前提,对JMM进行较为全面的分析,本篇的写作思路是先阐明Java内存区域划分、硬件内存架构、Java多线程的实现原理与Java内存模型的具体关系,在弄明白它们间的关系后,进一步分析Java内存模型作用以及一些必要的实现手段,以下是本篇主要内容(如有错误,欢迎留言,谢谢!)

    理解Java内存区域与Java内存模型

    Java内存区域

    Java虚拟机在运行程序时会把其自动管理的内存划分为以上几个区域,每个区域都有的用途以及创建销毁的时机,其中蓝色部分代表的是所有线程共享的数据区域,而绿色部分代表的是每个线程的私有数据区域。

    • 方法区(Method Area):

      方法区属于线程共享的内存区域,又称Non-Heap(非堆),主要用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,根据Java 虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError 异常。值得注意的是在方法区中存在一个叫运行时常量池(Runtime Constant Pool)的区域,它主要用于存放编译器生成的各种字面量和符号引用,这些内容将在类加载后存放到运行时常量池中,以便后续使用。

    • JVM堆(Java Heap):

      Java 堆也是属于线程共享的内存区域,它在虚拟机启动时创建,是Java 虚拟机所管理的内存中最大的一块,主要用于存放对象实例,几乎所有的对象实例都在这里分配内存,注意Java 堆是垃圾收集器管理的主要区域,因此很多时候也被称做GC 堆,如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError 异常。

    • 程序计数器(Program Counter Register):

      属于线程私有的数据区域,是一小块内存空间,主要代表当前线程所执行的字节码行号指示器。字节码解释器工作时,通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

    • 虚拟机栈(Java Virtual Machine Stacks):

      属于线程私有的数据区域,与线程同时创建,总数与线程关联,代表Java方法执行的内存模型。每个方法执行时都会创建一个栈桢来存储方法的的变量表、操作数栈、动态链接方法、返回值、返回地址等信息。每个方法从调用直结束就对于一个栈桢在虚拟机栈中的入栈和出栈过程,如下(图有误,应该为栈桢):

    • 本地方法栈(Native Method Stacks):

      本地方法栈属于线程私有的数据区域,这部分主要与虚拟机用到的 Native 方法相关,一般情况下,我们无需关心此区域。

    这里之所以简要说明这部分内容,注意是为了区别Java内存模型与Java内存区域的划分,毕竟这两种划分是属于不同层次的概念。

    Java内存模型概述

    Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝,前面说过,工作内存是每个线程的私有数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图

    需要注意的是,JMM与Java内存区域的划分是不同的概念层次,更恰当说JMM描述的是一组规则,通过这组规则控制程序中各个变量在共享数据区域和私有数据区域的访问方式,JMM是围绕原子性,有序性、可见性展开的(稍后会分析)。JMM与Java内存区域唯一相似点,都存在共享数据区域和私有数据区域,在JMM中主内存属于共享数据区域,从某个程度上讲应该包括了堆和方法区,而工作内存数据线程私有数据区域,从某个程度上讲则应该包括程序计数器、虚拟机栈以及本地方法栈。或许在某些地方,我们可能会看见主内存被描述为堆内存,工作内存被称为线程栈,实际上他们表达的都是同一个含义。关于JMM中的主内存和工作内存说明如下

    • 主内存

      主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量),当然也包括了共享的类信息、常量、静态变量。由于是共享数据区域,多条线程对同一个变量进行访问可能会发现线程安全问题。

    • 工作内存

      主要存储当前方法的所有本地变量信息(工作内存中存储着主内存中的变量副本拷贝),每个线程只能访问自己的工作内存,即线程中的本地变量对其它线程是不可见的,就算是两个线程执行的是同一段代码,它们也会各自在自己的工作内存中创建属于当前线程的本地变量,当然也包括了字节码行号指示器、相关Native方法的信息。注意由于工作内存是每个线程的私有数据,线程间无法相互访问工作内存,因此存储在工作内存的数据不存在线程安全问题。

    弄清楚主内存和工作内存后,接了解一下主内存与工作内存的数据存储类型以及操作方式,根据虚拟机规范,对于一个实例对象中的成员方法而言,如果方法中包含本地变量是基本数据类型(boolean,byte,short,char,int,long,float,double),将直接存储在工作内存的帧栈结构中,但倘若本地变量是引用类型,那么该变量的引用会存储在功能内存的帧栈中,而对象实例将存储在主内存(共享数据区域,堆)中。但对于实例对象的成员变量,不管它是基本数据类型或者包装类型(Integer、Double等)还是引用类型,都会被存储到堆区。至于static变量以及类本身相关信息将会存储在主内存中。需要注意的是,在主内存中的实例对象可以被多线程共享,倘若两个线程同时调用了同一个对象的同一个方法,那么两条线程会将要操作的数据拷贝一份到自己的工作内存中,执行完成操作后才刷新到主内存,简单示意图如下所示:

    硬件内存架构与Java内存模型

    硬件内存架构

    正如上图所示,经过简化CPU与内存操作的简易图,实际上没有这么简单,这里为了理解方便,我们省去了南北桥并将三级缓存统一为CPU缓存(有些CPU只有二级缓存,有些CPU有三级缓存)。就目前计算机而言,一般拥有多个CPU并且每个CPU可能存在多个核心,多核是指在一枚处理器(CPU)中集成两个或多个完整的计算引擎(内核),这样就可以支持多任务并行执行,从多线程的调度来说,每个线程都会映射到各个CPU核心中并行运行。在CPU内部有一组CPU寄存器,寄存器是cpu直接访问和处理的数据,是一个临时放数据的空间。一般CPU都会从内存取数据到寄存器,然后进行处理,但由于内存的处理速度远远低于CPU,导致CPU在处理指令时往往花费很多时间在等待内存做准备工作,于是在寄存器和主内存间添加了CPU缓存,CPU缓存比较小,但访问速度比主内存快得多,如果CPU总是操作主内存中的同一址地的数据,很容易影响CPU执行速度,此时CPU缓存就可以把从内存提取的数据暂时保存起来,如果寄存器要取内存中同一位置的数据,直接从缓存中提取,无需直接从主内存取。需要注意的是,寄存器并不每次数据都可以从缓存中取得数据,万一不是同一个内存地址中的数据,那寄存器还必须直接绕过缓存从内存中取数据。所以并不每次都得到缓存中取数据,这种现象有个专业的名称叫做缓存的命中率,从缓存中取就命中,不从缓存中取从内存中取,就没命中,可见缓存命中率的高低也会影响CPU执行性能,这就是CPU、缓存以及主内存间的简要交互过程,总而言之当一个CPU需要访问主存时,会先读取一部分主存数据到CPU缓存(当然如果CPU缓存中存在需要的数据就会直接从缓存获取),进而在读取CPU缓存到寄存器,当CPU需要写数据到主存时,同样会先刷新寄存器中的数据到CPU缓存,然后再把数据刷新到主内存中。

    Java线程与硬件处理器

    了解完硬件的内存架构后,接着了解JVM中线程的实现原理,理解线程的实现原理,有助于我们了解Java内存模型与硬件内存架构的关系,在Window系统和Linux系统上,Java线程的实现是基于一对一的线程模型,所谓的一对一模型,实际上就是通过语言级别层面程序去间接调用系统内核的线程模型,即我们在使用Java线程时,Java虚拟机内部是转而调用当前操作系统的内核线程来完成当前任务。这里需要了解一个术语,内核线程(Kernel-Level Thread,KLT),它是由操作系统内核(Kernel)支持的线程,这种线程是由操作系统内核来完成线程切换,内核通过操作调度器进而对线程执行调度,并将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这也就是操作系统可以同时处理多任务的原因。由于我们编写的多线程程序属于语言层面的,程序一般不会直接去调用内核线程,取而代之的是一种轻量级的进程(Light Weight Process),也是通常意义上的线程,由于每个轻量级进程都会映射到一个内核线程,因此我们可以通过轻量级进程调用内核线程,进而由操作系统内核将任务映射到各个处理器,这种轻量级进程与内核线程间1对1的关系就称为一对一的线程模型。如下图

    如图所示,每个线程最终都会映射到CPU中进行处理,如果CPU存在多核,那么一个CPU将可以并行执行多个线程任务。

    Java内存模型与硬件内存架构的关系

    通过对前面的硬件内存架构、Java内存模型以及Java多线程的实现原理的了解,我们应该已经意识到,多线程的执行最终都会映射到硬件处理器上进行执行,但Java内存模型和硬件内存架构并不完全一致。对于硬件内存来说只有寄存器、缓存内存、主内存的概念,并没有工作内存(线程私有数据区域)和主内存(堆内存)之分,也就是说Java内存模型对内存的划分对硬件内存并没有任何影响,因为JMM只是一种抽象的概念,是一组规则,并不实际存在,不管是工作内存的数据还是主内存的数据,对于计算机硬件来说都会存储在计算机主内存中,当然也有可能存储到CPU缓存或者寄存器中,因此总体上来说,Java内存模型和计算机硬件内存架构是一个相互交叉的关系,是一种抽象概念划分与真实物理硬件的交叉。(注意对于Java内存区域划分也是同样的道理)

    JMM存在的必要性

    在明白了Java内存区域划分、硬件内存架构、Java多线程的实现原理与Java内存模型的具体关系后,接着来谈谈Java内存模型存在的必要性。由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,线程与主内存中的变量操作必须通过工作内存间接完成,主要过程是将变量从主内存拷贝的每个线程各自的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,如果存在两个线程同时对一个主内存中的实例对象的变量进行操作就有可能诱发线程安全问题。如下图,主内存中存在一个共享变量x,现在有A和B两条线程分别对该变量x=1进行操作,A/B线程各自的工作内存中存在共享变量副本x。假设现在A线程想要修改x的值为2,而B线程却想要读取x的值,那么B线程读取到的值是A线程更新后的值2还是更新前的值1呢?答案是,不确定,即B线程有可能读取到A线程更新前的值1,也有可能读取到A线程更新后的值2,这是因为工作内存是每个线程私有的数据区域,而线程A变量x时,首先是将变量从主内存拷贝到A线程的工作内存中,然后对变量进行操作,操作完成后再将变量x写回主内,而对于B线程的也是类似的,这样就有可能造成主内存与工作内存间数据存在一致性问题,假如A线程修改完后正在将数据写回主内存,而B线程此时正在读取主内存,即将x=1拷贝到自己的工作内存中,这样B线程读取到的值就是x=1,但如果A线程已将x=2写回主内存后,B线程才开始读取的话,那么此时B线程读取到的就是x=2,但到底是哪种情况先发生呢?这是不确定的,这也就是所谓的线程安全问题。

    为了解决类似上述的问题,JVM定义了一组规则,通过这组规则来决定一个线程对共享变量的写入何时对另一个线程可见,这组规则也称为Java内存模型(即JMM),JMM是围绕着程序执行的原子性、有序性、可见性展开的,下面我们看看这三个特性。

    Java内存模型的承诺

    这里我们先来了解几个概念,即原子性?可见性?有序性?最后再阐明JMM是如何保证这3个特性。

    原子性

    原子性指的是一个操作是不可中断的,即使是在多线程环境下,一个操作一旦开始就不会被其他线程影响。比如对于一个静态变量int x,两条线程同时对他赋值,线程A赋值为1,而线程B赋值为2,不管线程如何运行,最终x的值要么是1,要么是2,线程A和线程B间的操作是没有干扰的,这就是原子性操作,不可被中断的特点。有点要注意的是,对于32位系统的来说,long类型数据和double类型数据(对于基本数据类型,byte,short,int,float,boolean,char读写是原子操作),它们的读写并非原子性的,也就是说如果存在两条线程同时对long类型或者double类型的数据进行读写是存在相互干扰的,因为对于32位虚拟机来说,每次原子读写是32位的,而long和double则是64位的存储单元,这样会导致一个线程在写时,操作完前32位的原子操作后,轮到B线程读取时,恰好只读取到了后32位的数据,这样可能会读取到一个既非原值又不是线程修改值的变量,它可能是“半个变量”的数值,即64位数据被两个线程分成了两次读取。但也不必太担心,因为读取到“半个变量”的情况比较少见,至少在目前的商用的虚拟机中,几乎都把64位的数据的读写操作作为原子操作来执行,因此对于这个问题不必太在意,知道这么回事即可。

    理解指令重排

    计算机在执行程序时,为了提高性能,编译器和处理器的常常会对指令做重排,一般分以下3种

    • 编译器优化的重排

      编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。

    • 指令并行的重排

      现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性(即后一个执行的语句无需依赖前面执行的语句的结果),处理器可以改变语句对应的机器指令的执行顺序

    • 内存系统的重排

      由于处理器使用缓存和读写缓存冲区,这使得加载(load)和存储(store)操作看上去可能是在乱序执行,因为三级缓存的存在,导致内存与缓存的数据同步存在时间差。

    其中编译器优化的重排属于编译期重排,指令并行的重排和内存系统的重排属于处理器重排,在多线程环境中,这些重排优化可能会导致程序出现内存可见性问题,下面分别阐明这两种重排优化可能带来的问题

    编译器重排

    下面我们简单看一个编译器重排的例子:

    线程 1             线程 2
    1: x2 = a ;      3: x1 = b ;
    2: b = 1;         4: a = 2 ;

    两个线程同时执行,分别有1、2、3、4四段执行代码,其中1、2属于线程1 , 3、4属于线程2 ,从程序的执行顺序上看,似乎不太可能出现x1 = 1 和x2 = 2 的情况,但实际上这种情况是有可能发现的,因为如果编译器对这段程序代码执行重排优化后,可能出现下列情况

    线程 1              线程 2
    2: b = 1;          4: a = 2 ; 
    1:x2 = a ;        3: x1 = b ;

    这种执行顺序下就有可能出现x1 = 1 和x2 = 2 的情况,这也就说明在多线程环境下,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的。

    处理器指令重排

    先了解一下指令重排的概念,处理器指令重排是对CPU的性能优化,从指令的执行角度来说一条指令可以分为多个步骤完成,如下

    • 取指 IF
    • 译码和取寄存器操作数 ID
    • 执行或者有效地址计算 EX
    • 存储器访问 MEM
    • 写回 WB

    CPU在工作时,需要将上述指令分为多个步骤依次执行(注意硬件不同有可能不一样),由于每一个步会使用到不同的硬件操作,比如取指时会只有PC寄存器和存储器,译码时会执行到指令寄存器组,执行时会执行ALU(算术逻辑单元)、写回时使用到寄存器组。为了提高硬件利用率,CPU指令是按流水线技术来执行的,如下:

    从图中可以看出当指令1还未执行完成时,第2条指令便利用空闲的硬件开始执行,这样做是有好处的,如果每个步骤花费1ms,那么如果第2条指令需要等待第1条指令执行完成后再执行的话,则需要等待5ms,但如果使用流水线技术的话,指令2只需等待1ms就可以开始执行了,这样就能大大提升CPU的执行性能。虽然流水线技术可以大大提升CPU的性能,但不幸的是一旦出现流水中断,所有硬件设备将会进入一轮停顿期,当再次弥补中断点可能需要几个周期,这样性能损失也会很大,就好比工厂组装手机的流水线,一旦某个零件组装中断,那么该零件往后的工人都有可能进入一轮或者几轮等待组装零件的过程。因此我们需要尽量阻止指令中断的情况,指令重排就是其中一种优化中断的手段,我们通过一个例子来阐明指令重排是如何阻止流水线技术中断的

    a = b + c ;
    d = e + f ;

    下面通过汇编指令展示了上述代码在CPU执行的处理过程

    • LW指令 表示 load,其中LW R1,b表示把b的值加载到寄存器R1中
    • LW R2,c 表示把c的值加载到寄存器R2中
    • ADD 指令表示加法,把R1 、R2的值相加,并存入R3寄存器中。
    • SW 表示 store 即将 R3寄存器的值保持到变量a中
    • LW R4,e 表示把e的值加载到寄存器R4中
    • LW R5,f 表示把f的值加载到寄存器R5中
    • SUB 指令表示减法,把R4 、R5的值相减,并存入R6寄存器中。
    • SW d,R6 表示将R6寄存器的值保持到变量d中

    上述便是汇编指令的执行过程,在某些指令上存在X的标志,X代表中断的含义,也就是只要有X的地方就会导致指令流水线技术停顿,同时也会影响后续指令的执行,可能需要经过1个或几个指令周期才可能恢复正常,那为什么停顿呢?这是因为部分数据还没准备好,如执行ADD指令时,需要使用到前面指令的数据R1,R2,而此时R2的MEM操作没有完成,即未拷贝到存储器中,这样加法计算就无法进行,必须等到MEM操作完成后才能执行,也就因此而停顿了,其他指令也是类似的情况。前面阐述过,停顿会造成CPU性能下降,因此我们应该想办法消除这些停顿,这时就需要使用到指令重排了,如下图,既然ADD指令需要等待,那我们就利用等待的时间做些别的事情,如把LW R4,eLW R5,f 移动到前面执行,毕竟LW R4,eLW R5,f执行并没有数据依赖关系,对他们有数据依赖关系的SUB R6,R5,R4指令在R4,R5加载完成后才执行的,没有影响,过程如下:

    正如上图所示,所有的停顿都完美消除了,指令流水线也无需中断了,这样CPU的性能也能带来很好的提升,这就是处理器指令重排的作用。关于编译器重排以及指令重排(这两种重排我们后面统一称为指令重排)相关内容已阐述清晰了,我们必须意识到对于单线程而已指令重排几乎不会带来任何影响,比竟重排的前提是保证串行语义执行的一致性,但对于多线程环境而已,指令重排就可能导致严重的程序轮序执行问题,如下

    class MixedOrder{
        int a = 0;
        boolean flag = false;
        public void writer(){
            a = 1;
            flag = true;
        }
    
        public void read(){
            if(flag){
                int i = a + 1;
            }
        }
    }

    如上述代码,同时存在线程A和线程B对该实例对象进行操作,其中A线程调用写入方法,而B线程调用读取方法,由于指令重排等原因,可能导致程序执行顺序变为如下:

     线程A                    线程B
     writer:                 read:
     1:flag = true;           1:flag = true;
     2:a = 1;                 2: a = 0 ; //误读
                              3: i = 1 ;

    由于指令重排的原因,线程A的flag置为true被提前执行了,而a赋值为1的程序还未执行完,此时线程B,恰好读取flag的值为true,直接获取a的值(此时B线程并不知道a为0)并执行i赋值操作,结果i的值为1,而不是预期的2,这就是多线程环境下,指令重排导致的程序乱序执行的结果。因此,请记住,指令重排只会保证单线程中串行语义的执行的一致性,但并不会关心多线程间的语义一致性。

    可见性

    理解了指令重排现象后,可见性容易了,可见性指的是当一个线程修改了某个共享变量的值,其他线程是否能够马上得知这个修改的值。对于串行程序来说,可见性是不存在的,因为我们在任何一个操作中修改了某个变量的值,后续的操作中都能读取这个变量值,并且是修改过的新值。但在多线程环境中可就不一定了,前面我们分析过,由于线程对共享变量的操作都是线程拷贝到各自的工作内存进行操作后才写回到主内存中的,这就可能存在一个线程A修改了共享变量x的值,还未写回主内存时,另外一个线程B又对主内存中同一个共享变量x进行操作,但此时A线程工作内存中共享变量x对线程B来说并不可见,这种工作内存与主内存同步延迟现象就造成了可见性问题,另外指令重排以及编译器优化也可能导致可见性问题,通过前面的分析,我们知道无论是编译器优化还是处理器优化的重排现象,在多线程环境下,确实会导致程序轮序执行的问题,从而也就导致可见性问题。

    有序性

    有序性是指对于单线程的执行代码,我们总是认为代码的执行是按顺序依次执行的,这样的理解并没有毛病,毕竟对于单线程而言确实如此,但对于多线程环境,则可能出现乱序现象,因为程序编译成机器码指令后可能会出现指令重排现象,重排后的指令与原指令的顺序未必一致,要明白的是,在Java程序中,倘若在本线程内,所有操作都视为有序行为,如果是多线程环境下,一个线程中观察另外一个线程,所有操作都是无序的,前半句指的是单线程内保证串行语义执行的一致性,后半句则指指令重排现象和工作内存与主内存同步延迟现象。

    JMM提供的解决方案

    在理解了原子性,可见性以及有序性问题后,看看JMM是如何保证的,在Java内存模型中都提供一套解决方案供Java工程师在开发过程使用,如原子性问题,除了JVM自身提供的对基本数据类型读写操作的原子性外,对于方法级别或者代码块级别的原子性操作,可以使用synchronized关键字或者重入锁(ReentrantLock)保证程序执行的原子性,关于synchronized的详解,看博主另外一篇文章( 深入理解Java并发之synchronized实现原理)。而工作内存与主内存同步延迟现象导致的可见性问题,可以使用synchronized关键字或者volatile关键字解决,它们都可以使一个线程修改后的变量立即对其他线程可见。对于指令重排导致的可见性问题和有序性问题,则可以利用volatile关键字解决,因为volatile的另外一个作用就是禁止重排序优化,关于volatile稍后会进一步分析。除了靠sychronized和volatile关键字来保证原子性、可见性以及有序性外,JMM内部还定义一套happens-before 原则来保证多线程环境下两个操作间的原子性、可见性以及有序性。

    理解JMM中的happens-before 原则

    倘若在程序开发中,仅靠sychronized和volatile关键字来保证原子性、可见性以及有序性,那么编写并发程序可能会显得十分麻烦,幸运的是,在Java内存模型中,还提供了happens-before 原则来辅助保证程序执行的原子性、可见性以及有序性的问题,它是判断数据是否存在竞争、线程是否安全的依据,happens-before 原则内容如下

    • 程序顺序原则,即在一个线程内必须保证语义串行性,也就是说按照代码顺序执行。

    • 锁规则 解锁(unlock)操作必然发生在后续的同一个锁的加锁(lock)之前,也就是说,如果对于一个锁解锁后,再加锁,那么加锁的动作必须在解锁动作之后(同一个锁)。

    • volatile规则 volatile变量的写,先发生于读,这保证了volatile变量的可见性,简单的理解就是,volatile变量在每次被线程访问时,都强迫从主内存中读该变量的值,而当该变量发生变化时,又会强迫将最新的值刷新到主内存,任何时刻,不同的线程总是能够看到该变量的最新值。

    • 线程启动规则 线程的start()方法先于它的每一个动作,即如果线程A在执行线程B的start方法之前修改了共享变量的值,那么当线程B执行start方法时,线程A对共享变量的修改对线程B可见

    • 传递性 A先于B ,B先于C 那么A必然先于C

    • 线程终止规则 线程的所有操作先于线程的终结,Thread.join()方法的作用是等待当前执行的线程终止。假设在线程B终止之前,修改了共享变量,线程A从线程B的join方法成功返回后,线程B对共享变量的修改将对线程A可见。

    • 线程中断规则 对线程 interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测线程是否中断。

    • 对象终结规则 对象的构造函数执行,结束先于finalize()方法

    上述8条原则无需手动添加任何同步手段(synchronized|volatile)即可达到效果,下面我们结合前面的案例演示这8条原则如何判断线程是否安全,如下:

    class MixedOrder{
        int a = 0;
        boolean flag = false;
        public void writer(){
            a = 1;
            flag = true;
        }
    
        public void read(){
            if(flag){
                int i = a + 1;
            }
        }
    }

    同样的道理,存在两条线程A和B,线程A调用实例对象的writer()方法,而线程B调用实例对象的read()方法,线程A先启动而线程B后启动,那么线程B读取到的i值是多少呢?现在依据8条原则,由于存在两条线程同时调用,因此程序次序原则不合适。writer()方法和read()方法都没有使用同步手段,锁规则也不合适。没有使用volatile关键字,volatile变量原则不适应。线程启动规则、线程终止规则、线程中断规则、对象终结规则、传递性和本次测试案例也不合适。线程A和线程B的启动时间虽然有先后,但线程B执行结果却是不确定,也是说上述代码没有适合8条原则中的任意一条,也没有使用任何同步手段,所以上述的操作是线程不安全的,因此线程B读取的值自然也是不确定的。修复这个问题的方式很简单,要么给writer()方法和read()方法添加同步手段,如synchronized或者给变量flag添加volatile关键字,确保线程A修改的值对线程B总是可见。

    volatile内存语义

    volatile在并发编程中很常见,但也容易被滥用,现在我们就进一步分析volatile关键字的语义。volatile是Java虚拟机提供的轻量级的同步机制。volatile关键字有如下两个作用

    • 保证被volatile修饰的共享gong’x变量对所有线程总数可见的,也就是当一个线程修改了一个被volatile修饰共享变量的值,新值总数可以被其他线程立即得知。

    • 禁止指令重排序优化。

    volatile的可见性

    关于volatile的可见性作用,我们必须意识到被volatile修饰的变量对所有线程总数立即可见的,对volatile变量的所有写操作总是能立刻反应到其他线程中,但是对于volatile变量运算操作在多线程环境并不保证安全性,如下

    public class VolatileVisibility {
        public static volatile int i =0;
    
        public static void increase(){
            i++;
        }
    }

    正如上述代码所示,i变量的任何改变都会立马反应到其他线程中,但是如此存在多条线程同时调用increase()方法的话,就会出现线程安全问题,毕竟i++;操作并不具备原子性,该操作是先读取值,然后写回一个新值,相当于原来的值加上1,分两步完成,如果第二个线程在第一个线程读取旧值和写回新值期间读取i的域值,那么第二个线程就会与第一个线程一起看到同一个值,并执行相同值的加1操作,这也就造成了线程安全失败,因此对于increase方法必须使用synchronized修饰,以便保证线程安全,需要注意的是一旦使用synchronized修饰方法后,由于synchronized本身也具备与volatile相同的特性,即可见性,因此在这样种情况下就完全可以省去volatile修饰变量。

    public class VolatileVisibility {
        public static int i =0;
    
        public synchronized static void increase(){
            i++;
        }
    }

    现在来看另外一种场景,可以使用volatile修饰变量达到线程安全的目的,如下

    public class VolatileSafe {
    
        volatile boolean close;
    
        public void close(){
            close=true;
        }
    
        public void doWork(){
            while (!close){
                System.out.println("safe....");
            }
        }
    }

    由于对于boolean变量close值的修改属于原子性操作,因此可以通过使用volatile修饰变量close,使用该变量对其他线程立即可见,从而达到线程安全的目的。那么JMM是如何实现让volatile变量对其他线程立即可见的呢?实际上,当写一个volatile变量时,JMM会把该线程对应的工作内存中的共享变量值刷新到主内存中,当读取一个volatile变量时,JMM会把该线程对应的工作内存置为无效,那么该线程将只能从主内存中重新读取共享变量。volatile变量正是通过这种写-读方式实现对其他线程可见(但其内存语义实现则是通过内存屏障,稍后会说明)。

    volatile禁止重排优化

    volatile关键字另一个作用就是禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象,关于指令重排优化前面已详细分析过,这里主要简单说明一下volatile是如何实现禁止指令重排优化的。先了解一个概念,内存屏障(Memory Barrier)。
    内存屏障,又称内存栅栏,是一个CPU指令,它的作用有两个,一是保证特定操作的执行顺序,二是保证某些变量的内存可见性(利用该特性实现volatile的内存可见性)。由于编译器和处理器都能执行指令重排优化。如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重排序,也就是说通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化。Memory Barrier的另外一个作用是强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本。总之,volatile变量正是通过内存屏障实现其在内存中的语义,即可见性和禁止重排优化。下面看一个非常典型的禁止重排优化的例子DCL,如下:

    /**
     * Created by zejian on 2017/6/11.
     * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
     */
    public class DoubleCheckLock {
    
        private static DoubleCheckLock instance;
    
        private DoubleCheckLock(){}
    
        public static DoubleCheckLock getInstance(){
    
            //第一次检测
            if (instance==null){
                //同步
                synchronized (DoubleCheckLock.class){
                    if (instance == null){
                        //多线程环境下可能会出现问题的地方
                        instance = new DoubleCheckLock();
                    }
                }
            }
            return instance;
        }
    }

    上述代码一个经典的单例的双重检测的代码,这段代码在单线程环境下并没有什么问题,但如果在多线程环境下就可以出现线程安全问题。原因在于某一个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化。因为instance = new DoubleCheckLock();可以分为以下3步完成(伪代码)

    memory = allocate(); //1.分配对象内存空间
    instance(memory);    //2.初始化对象
    instance = memory;   //3.设置instance指向刚分配的内存地址,此时instance!=null

    由于步骤1和步骤2间可能会重排序,如下:

    memory = allocate(); //1.分配对象内存空间
    instance = memory;   //3.设置instance指向刚分配的内存地址,此时instance!=null,但是对象还没有初始化完成!
    instance(memory);    //2.初始化对象

    由于步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。但是指令重排只会保证串行语义的执行的一致性(单线程),但并不会关心多线程间的语义一致性。所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,也就造成了线程安全问题。那么该如何解决呢,很简单,我们使用volatile禁止instance变量被执行指令重排优化即可。

      //禁止指令重排优化
      private volatile static DoubleCheckLock instance;

    ok~,到此相信我们对Java内存模型和volatile应该都有了比较全面的认识,总而言之,我们应该清楚知道,JMM就是一组规则,这组规则意在解决在并发编程可能出现的线程安全问题,并提供了内置解决方案(happen-before原则)及其外部可使用的同步手段(synchronized/volatile等),确保了程序执行在多线程环境中的应有的原子性,可视性及其有序性。

    如有错误,欢迎留言,谢谢!

    参考资料:
    http://tutorials.jenkov.com/java-concurrency/java-memory-model.html
    http://blog.csdn.net/iter_zc/article/details/41843595
    http://ifeve.com/wp-content/uploads/2014/03/JSR133%E4%B8%AD%E6%96%87%E7%89%881.pdf

    《深入理解JVM虚拟机》
    《Java高并发程序设计》

    展开全文
  • 内存测试_内存检测工具大全

    万次阅读 2020-01-25 22:12:05
    测试内存,往往不局限于一种软件,因为每种工具都有自己的局限性。灵活运用多种工具,是为了实现最大的测试效果,提升产品质量 1、《HCI MemTest》 https://hcidesign.com/memtest/ HCI MemTest恐怕是世界上...

    测试内存,往往不局限于一种软件,因为每种工具都有自己的局限性。灵活运用多种工具,可以实现效益最大化

    1、《HCI MemTest》

    https://hcidesign.com/memtest/

    HCI MemTest恐怕是世界上用户最多的内存测试软件

    它之所以如此出名,我认为有以下几点原因:

    ①它是当年少数可以在Windows操作系统中运行的内存检测软件之一(传统的内存测试均是在DOS或Linux下实现的字符界面)

    ②该软件使用非常简单,并且在网上可以随意搜索下载到汉化破解版本。很多盗版系统光盘、PE工具里面都集成了该工具

    它的免费版本需要手动多开(Free版有2GB限制),于是有国人针对的做了MEMTEST多开器《RunMemtest PRO》(噹)

    HCI MemTest适合作为内存稳定性、兼容性测试工具,特别适合用于验证内存超频稳定性

    HCI MemTest是大多数电商PC售后客服的标配工具(因为客户操作起来简单,无需做启动盘)。根据经验,测试到100%可诊断出明显的内存故障;检测间歇性暗病的测试率要达到400%。此工具可以测试出百分之九十以上的故障条。注意:在云更新无盘环境下,因为部分内存作回写缓存,应用层不可访问,HCI MemTest存在误报的现象

    2、《MEMTEST64》

    https://www.techpowerup.com/memtest64/

    它是鼎鼎大名的GPU-Z团队开发,同样是免费的,可以下载到汉化版。支持随机数据测试、移动数据测试、位模式测试

    它的优点:

    ①64位访问内存,无须像HCI MemTest那样开多个窗口(故不支持32位系统)。支持CPU多线程,测试速度比同类软件快

    ②测试内存所占大小可选择(一般验证超频不需要做完整大小测试,至少15%区域即可)

    ③可指定测试次数和测试时间。可让错误时停止测试,避免过多触发内存坏块而导致蓝屏

    它的缺点:

    ①在WIN10下,有较大几率会出现“内存锁定失败(可能由其它应用程序/内核保留)”,也就是分配内存失败,需要重启再点测试

    ②在WIN10下,有较大几率会出现分配占用内存过小的问题,这意味着不能检查所有区域,需要重启再点测试

    ③在WIN10下,测试内存会伴随着较大的硬盘写入(可能与“将其他应用程序推送到硬盘虚拟内存中以释放物理内存有关”)

    威刚售后客服曾说MEMTEST64与1809系统不大兼容。笔者用此工具做内存兼容性、稳定性测试,也用来测试HCI MemTest、MEMTEST86检测不出的坏件,发现此工具对镁光(英睿达)铂胜马甲内存、七彩虹内存(4bit颗粒)十分敏感,一分钟左右即可将故障显现。

    三、AIDA64稳定性测试

    http://www.aida64.com.cn

    Aida64测试工具有一项System Stability Test,可以测试稳定性和兼容性,不过国人一般用它来测试散热(比如单拷FPU)。AIDA64的测试压力强度确实不如LINX和Prime95,不大适合作为内存超频验证工具。笔者用它来做内存稳定兼容性测试工具,也用于测试MEMTEST扫不出故障的内存条。在遇到硬件故障时,AIDA64通常会报以下错误:

    测内存可单独勾选“Stress System Memory“

    AIDA64的优势在于可以实现自动化测试,其命令行使用极其方便(仅支持商业版和工程师版)

    @echo off
    :start
    AIDA64 /SST CPU,FPU,CACHE,RAM /SSTDUR 15 
    echo System Stability Test Pass
    pause

    SST后面表示要测试的硬件项目,SSTDUR后面表示要测试的时间。即测试15分钟后终止测试,并显示测试结果

    上述三种,它们属于应用层内存检测工具。这些设计者无须关心DDR是几代、芯片组型号、CPU型号、是否ECC、主板BIOS等硬件底层的因素,只需要关心校验算法和优化检测效率。即使在这篇文章写下之时,DDR5还没有上市,但可以断言,上述三种工具,可以测试DDR5内存。而无须随着DDR5内存的上市而进行版本迭代。应用层面的内存检测几乎不存在误报现象,但漏报率大于专业的内存诊断软件。由于现代操作系统会占据大量的内存,导致某些内存区域无法被访问检测到

    下面再来介绍几种专业工具:

    四、《MEMTEST86》与《MEMTEST86+》

    https://www.memtest86.com

    http://www.memtest.org/

    MemTest86最初由Chris Brady(克里斯·布雷迪)开发,于1994年首次发布。但是,所使用的某些测试算法可以追溯到1981年,MemTest86最初是作为免费开源(GPL)软件发布的。后来在3.0版停止更新了两年,为了支持新的硬件,Samuel Demeulemeester在Memtest86的基础上开发了Memtest86+,命名很不幸,因为它导致了现在用户认知的混乱。不管怎样,随后双方都继续开发各自的MEMTEST86。MemTest86 + V5.0和MemTest86 V4.3的功能集在2013年大致相等。也是在2013年,有人提出将两个代码流重新合并到一个程序包中的提议,但Samuel希望不合并这些版本。后来Chris Brady将Memtest86的版权卖给了PassMark(世界上著名的硬件测试软体开发商)。Passmark在原有的代码之上,增加了64位支持(无须PAE)、UEFI自启动、DDR4支持,并且注册Memtest86为商标,把MEMTEST86变为了闭源商业软件(对于普通用户是完全免费的)。而MemTest86+ 5.01版本的开发似乎已经停止。在撰写本文时(2020年2月),自2013年以来没有更新。2013年时,还处在DDR3时代,所以MEMTEST86+在测试某些DDR4平台的时候会遇到误报和漏报问题,所谓误报,就是内存无故障却报错;漏报,就是内存有故障,却不报错。这主要是由于缺乏硬件层面支持造成的。PassMark MEMTEST86在旧版V4的基础上增加了第13项测试 [Hammer Test],可以检测出更多有缺陷的颗粒。虽然不建议在DDR4上使用老旧的MEMTEST86+,但因为PassMark MEMTEST86不再支持传统BIOS启动(仅支持UEFI),所以在DDR1\DDR2\DDR3机器上,MemTest86+ 5.01仍然有一番用武之地。

    MEMTEST86扫描一根8G DDR3或8G DDR4仅需要不到30分钟,而同类产品往往需要一个小时

    PassMark MEMTEST86内存测试算法经过二十余年的不断完善,可以有效地识别99%以上的内存故障。因此,系统制造商,电脑维修店,超频者和PC制造商经常使用它,MEMTEST86几乎成了业界事实上的标准测试和验证工具,很多工厂级测试工具都是基于它开发的。大多数内存厂商在检测过程中也会用到它(特别是套条),如果你买的内存用MEMTEST86扫出有故障,那么内存厂商也是认可这个测试结果的。

    五、《微软内存诊断工具》

    微软内存诊断工具英文名称windows memory diagnostic tool,是最常见而又被忽视的内存测试利器。它最早集成于windows安装光盘,从VISTA时代开始内置于系统中。

    早期的界面是这样的:

    WIN7时代是这样的:

    开机windows启动菜单可以找到。进入系统后运行MdSched,也可以调出。

    默认设置是测不出细微故障的,需要按F1选择扩展测试。从算法上看,扩展测试精确度不错,但是十分耗时,8G内存测试长达一整晚时间

    基本:基本测试为 MATS+、INVC 和 SCHCKR(启用缓存)。标准:标准测试包括所有基本测试,以及 LRAND、Stride 6(启用缓存)、CHCKR3、WMATS+ 和 WINVC。扩展:扩展测试包括所有标准测试,外加 MATS+(禁用缓存)、Stride38、WSCHCKR、WStride-6、CHCKR4、WCHCKR3、ERAND、Stride6(禁用缓存)和 CHCKR8。

    六、《RAM STRESS TEST

    https://www.rst.com

    RST是一款由美国Ultra-X公司开发的工厂级测试工具,专为满足内存制造商、系统制造商、设计工程师和维修人员的严格测试需求而设计的。后来流落到民间,它能够检测出内存条是否存在颗粒损坏、PCB板有没有短路或虚焊等情况,据说镁光、海盗船、金士顿、海力士都是它的典型客户。自1999年推出以来,RST树立了测试准确性的标杆,并已成为业界事实上的标准测试和验证工具。RST拥有30多种专有测试模式,产品以硬件的形式售卖,有PCI、PCI-E、USB多种版本。具有无与伦比准确性。

    网上能找到最新的破解版是RST PRO USB3(2007年发布),最大内存容量64GB,不支持UEFI启动

    这个公司不卖软件,都是以硬件方式售卖,可以最大限度减少盗版。中国大陆买不到,而且售价很高,至少数百美金

    七、《AMT64MP》

    AMT64全称64-Bits Advanced Module Test program,是专为工厂及内存条商家开发的硬件测试卡,适合内存条加工厂及内存条零售市场商家使用。AMT64MP是在AMT64基础上的升级产品,支持多核心处理器,以加快检测速度。可以将故障定位到具体颗粒(需要特定主板的支持,比如B75\B85\Z170),一般作为维修工具。从测试项目上看,它借鉴了MEMTEST86、MEMTEST86+、RST的某些算法,测试项目16个,应该具有较高的精度和测试速度。笔者花68元从咸鱼上买了一块,在200系、300系主板上虽然不支持坏颗粒定位,但同样能够进行错误扫描。同样不支持UEFI启动,需要开启CSM方能启动。最新的产品名为AMT128,可支持在B360、Z370主板下定位坏颗粒

    八、《DocMemory》

    https://www.simmtester.com/ProductDocMemory

    DocMemory中文名为《内存神医》,它早期是一种免费软件,作者后期做成了硬件产品的形式。该公司研发重点在于内存测试仪,DocMemory测试卡自2009年就未再更新过了,最多支持到DDR3。DocMemory V3.1是能够下载到的绝版,不支持4G以上内存

     

    九、《RAMFIX V110516B》

    RAMFIX V110516B是国人基于MEMTEST86开源项目做出的一个测试软件,可定为到具体故障颗粒。不过已经多年没更新过了,已经被作者放弃了

    十、《Smart RAM Detect 1.1》

    https://www.ramcenter.com.tw

    Smart RAM Detect 1.1在谷歌上都很难找到资源了,是台湾瑞腾特开发的,不支持4GB以上内存。该公司目前只生产硬件产品了

    十一、《Memtest Jacky V1.6》

    在MEMTEST86基础上做的颗粒地址定位,DDR1以上内存可能无法精确定位坏颗粒

    十二、《GoldMemory

    http://www.goldmemory.cz

    GoldMemory是捷克人Michal Tulacek编写的,免费版6.92自2007年未再次更新过,付费版7.98支持DDR4,但也数年未更新

    此软件在东欧国家使用比较普遍,大多被集中到了盗版光盘中,可以从以下站点下载到7.85破解版:

    http://89.236.197.208/Public/BELOFF/Beloff%202015%20FUll/BOOT/IMG/GOLDMEM.IMG

    http://www.vvg.su/data/Data/Soft/BOOT/IMG/GOLDMEM.IMG

    基于PAE支持64G内存,支持快读、标准、完全三种测试模式,仅支持传统引导,不支持UEFI模式

    一路走来,我们发现很多优秀的软件都死掉了,研发不易,且行且珍惜

     

    展开全文
  • #内存泄露# linux常用内存相关命令

    万次阅读 2020-06-19 11:14:34
     free 命令会显示系统内存的使用情况,包括物理内存、交换内存(swap)和内核缓冲区内存等。 $ free total used free shared buff/cache available Mem: 8047480 6142828 154116 465584 1750536 1078080 Swap: ...

    Table of Contents

    free命令

    vmstat命令

    top命令

    cat /proc/meminfo

    ps aux命令


    free命令

      free 命令会显示系统内存的使用情况,包括物理内存、交换内存(swap)和内核缓冲区内存等。

    $ free
                  total        used        free      shared  buff/cache   available
    Mem:        8047480     6142828      154116      465584     1750536     1078080
    Swap:       8267772     1446400     6821372
    

    释义:

      Mem:内存使用情况。

      Swap:交换空间(虚拟内存)使用情况。

      total:系统总共可用物理内存、交换空间大小。

      used:已经被使用的物理内存、交换空间大小。

      free:剩余可用物理内存、交换空间大小。

      shared:被共享使用的物理内存大小。

      buff/cache:被 buffer 和 cache 使用的物理内存大小。

      available:还可以被应用程序使用的物理内存大小。

    
    free
                 total       used       free     shared    buffers     cached
    Mem:        601064     482856     118208      32648       1748     207788
    -/+ buffers/cache:     273320     327744
    Swap:            0          0          0
    

     释义:

    Mem行: 显示物理内存情况。

    列名含义
    total内存总量
    used使用内存
    free可用内存
    shared共享内存
    buffersbuffer缓存,可回收
    cachedcached缓存,可回收

    -/+buffers/cache行: 显示基于应用角度考虑的内存情况,也可理解为真实的内存使用情况。(计算已使用内存时减去buffers/cache,计算可使用内存时加上buffers/cache。)这里的buffers指Buffer cache(缓冲区缓存),cache指Page cache(页面缓存)。在当前的Linux内核中,Page cache是针对内存页的缓存。也就是说,如果有内存是以page进行分配管理的,都可以使用Page cache作为其缓存来管理使用。当然,不是所有的内存都是以页(page)进行管理的,也有很多是针对块(block),这部分内存使用如果要用到cache功能,则都集中到buffer cache中来使用。

    列名含义
    used实际使用内存
    free实际可用内存

      Mem行和buffers/cache行的各列计算关系如下:(M代表Mem行,B代表buffers/cache行)

    total(M) = used(m) + free(m)
    total(M) = used(B) + free(B) 
    used(M) = used(B) + buffers(M) + cached(M)
    free(B) = free(M) + buffers(M) + cached(M)

    Swap行: 显示交换空间使用情况,交换空间(swap space)是磁盘上的一块区域,可以是一个分区,也可以是一个文件。当系统物理内存吃紧时,Linux 会将内存中不常访问的数据保存到 swap 上,这样系统就有更多的物理内存为各个进程服务。而当系统需要访问swap上存储的内容时,再将swap 上的数据加载到内存中,这就是常说的换出和换入。交换空间可以在一定程度上缓解内存不足的情况,但是它需要读写磁盘数据,所以性能不是很高。

    列名含义
    used实际使用内存
    free实际可用内存

    free 命令中的信息都来自于 /proc/meminfo 文件。见cat /proc/meminfo

     

    常见用法:

    • free -k:按照g的计数方式来显示内存信息。
    • free -m:按照m的计数方式来显示内存信息。
    • free -g:按照g的计数方式来显示内存信息。
    • free -t:按照总和的形式显示内存的使用信息。
    • free -s 5:周期性展示内存使用情况,意思为每隔5秒统计展示,直到我们按下ctrl +c 键取消统计为止。
    • free -s 2 -c 4:周期性展示统计内存信息。本篇实例为每隔2秒统计一次,统计4次。
    • free -V:查看版本信息
    $ free --help
    
    Usage:
     free [options]
    
    Options:
     -b, --bytes         show output in bytes
     -k, --kilo          show output in kilobytes
     -m, --mega          show output in megabytes
     -g, --giga          show output in gigabytes
         --tera          show output in terabytes
     -h, --human         show human-readable output
         --si            use powers of 1000 not 1024
     -l, --lohi          show detailed low and high memory statistics
     -t, --total         show total for RAM + swap
     -s N, --seconds N   repeat printing every N seconds
     -c N, --count N     repeat printing N times, then exit
     -w, --wide          wide output
    
         --help     display this help and exit
     -V, --version  output version information and exit
    
    For more details see free(1).

    vmstat命令

      vmstat 是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对操作系统的虚拟内存、进程、CPU活动进行监控,是对系统的整体情况进行的统计。

    $ vmstat 
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     1  0 338176 143144 384176 2212096    0    0    43    19    3   25  3  1 96  0  0

    Procs(进程):

      r: 运行队列中进程数量

      b: 等待IO的进程数量

    Memory(内存):

      swpd: 使用虚拟内存大小

      free: 可用内存大小

      buff: 用作缓冲的内存大小

      cache: 用作缓存的内存大小

    Swap:

      si: 每秒从交换区写到内存的大小

      so: 每秒写入交换区的内存大小

    IO:(现在的Linux版本块的大小为1024bytes)

      bi: 每秒读取的块数

      bo: 每秒写入的块数

    系统:

    in: 每秒中断数,包括时钟中断。【interrupt】

    cs: 每秒上下文切换数。        【count/second】

    CPU(以百分比表示):

      us: 用户进程执行时间(user time)

      sy: 系统进程执行时间(system time)

      id: 空闲时间(包括IO等待时间),中央处理器的空闲时间 。以百分比表示。

      wa: 等待IO时间

    常见用法:

    • vmstat 1    //每隔1s打印一次
    • vmstat 1 5   //每隔1秒打印一次,打印五次
    • vmstat -s    //显示内存相关统计信息及多种系统活动数量
    $ vmstat --help
    
    Usage:
     vmstat [options] [delay [count]]
    
    Options:
     -a, --active           active/inactive memory
     -f, --forks            number of forks since boot
     -m, --slabs            slabinfo
     -n, --one-header       do not redisplay header
     -s, --stats            event counter statistics
     -d, --disk             disk statistics
     -D, --disk-sum         summarize disk statistics
     -p, --partition <dev>  partition specific statistics
     -S, --unit <char>      define display unit
     -w, --wide             wide output
     -t, --timestamp        show timestamp
    
     -h, --help     display this help and exit
     -V, --version  output version information and exit
    
    For more details see vmstat(8).

    top命令

      使用top命令,可以查看正在运行的进程和系统负载信息,包括cpu负载、内存使用、各个进程所占系统资源等,top命令以一定频率动态更新这些统计信息。

    $ top
    
    top - 11:06:47 up 3 days, 19:00,  7 users,  load average: 0.18, 0.21, 0.15
    Tasks: 280 total,   1 running, 225 sleeping,   0 stopped,   0 zombie
    %Cpu(s): 10.4 us,  1.0 sy,  0.0 ni, 88.2 id,  0.1 wa,  0.0 hi,  0.3 si,  0.0 st
    KiB Mem :  8047472 total,   142028 free,  5307464 used,  2597980 buff/cache
    KiB Swap:  8267772 total,  7929596 free,   338176 used.  1780512 avail Mem 
    
      PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                         
    10335 cll       20   0  214088  79724   4720 S  34.9  1.0  56:57.82 synergyc                                        
     1039 root      20   0  584644 217704 168232 S   3.0  2.7  59:35.71 Xorg                                            
     4697 root      20   0 1231768  35196   4216 S   2.7  0.4 216:14.55 vmware-hostd                                    
     1563 cll       20   0 1514936  86452  27684 S   2.3  1.1 123:58.21 compiz                                          
    29317 cll       20   0  642576  50860  20452 S   1.3  0.6  10:24.93 /usr/bin/x-term                                 
    18307 root      20   0       0      0      0 I   0.7  0.0   0:05.73 kworker/1:2                                     
      799 avahi     20   0   45572   3868   2912 S   0.3  0.0  24:05.96 avahi-daemon                                    
     2245 cll       20   0 1928640 218216  42808 S   0.3  2.7   8:18.58 TeamViewer                                      
     8399 root      20   0   15236   3844   2668 S   0.3  0.0   3:34.23 sh                                              
    20069 cll       20   0   43676   3708   3000 R   0.3  0.0   0:00.07 top                                             
    31219 cll       20   0  825672 232332  32188 S   0.3  2.9   8:05.88 code                                            
        1 root      20   0  185460   4692   2904 S   0.0  0.1   0:07.57 systemd                                         
        2 root      20   0       0      0      0 S   0.0  0.0   0:00.43 kthreadd                                        
        4 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 kworker/0:0H                                    
        6 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 mm_percpu_wq                                    
        7 root      20   0       0      0      0 S   0.0  0.0   0:03.95 ksoftirqd/0                                     
        8 root      20   0       0      0      0 I   0.0  0.0   9:40.74 rcu_sched                                       
        9 root      20   0       0      0      0 I   0.0  0.0   0:00.00 rcu_bh                                          
       10 root      rt   0       0      0      0 S   0.0  0.0   0:00.17 migration/0                                     
       11 root      rt   0       0      0      0 S   0.0  0.0   0:01.26 watchdog/0                                      
       12 root      20   0       0      0      0 S   0.0  0.0   0:00.00 cpuhp/0                                         
       13 root      20   0       0      0      0 S   0.0  0.0   0:00.00 cpuhp/1                                         
       14 root      rt   0       0      0      0 S   0.0  0.0   0:01.31 watchdog/1                                      
       15 root      rt   0       0      0      0 S   0.0  0.0   0:00.23 migration/1                                     
       16 root      20   0       0      0      0 S   0.0  0.0   0:07.94 ksoftirqd/1                                     
       18 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 kworker/1:0H                                    
    

    VIRT:virtual memory usage 虚拟内存
    1、进程“需要的”虚拟内存大小,包括进程使用的库、代码、数据等
    2、假如进程申请100m的内存,但实际只使用了10m,那么它会增长100m,而不是实际的使用量

    RES:resident memory usage 常驻内存
    1、进程当前使用的内存大小,但不包括swap out
    2、包含其他进程的共享
    3、如果申请100m的内存,实际使用10m,它只增长10m,与VIRT相反
    4、关于库占用内存的情况,它只统计加载的库文件所占内存大小

    SHR:shared memory 共享内存
    1、除了自身进程的共享内存,也包括其他进程的共享内存
    2、虽然进程只使用了几个共享库的函数,但它包含了整个共享库的大小
    3、计算某个进程所占的物理内存大小公式:RES – SHR
    4、swap out后,它将会降下来

    DATA
    1、数据占用的内存。如果top没有显示,按f键可以显示出来。
    2、真正的该程序要求的数据空间,是真正在运行中要使用的。

    top 运行中可以通过 top 的内部命令对进程的显示方式进行控制。内部命令如下:

    • s – 改变画面更新频率
    • l – 关闭或开启第一部分第一行 top 信息的表示
    • t – 关闭或开启第一部分第二行 Tasks 和第三行 Cpus 信息的表示
    • m – 关闭或开启第一部分第四行 Mem 和 第五行 Swap 信息的表示
    • N – 以 PID 的大小的顺序排列表示进程列表
    • P – 以 CPU 占用率大小的顺序排列进程列表
    • M – 以内存占用率大小的顺序排列进程列表
    • h – 显示帮助
    • n – 设置在进程列表所显示进程的数量
    • q – 退出 top
    • s – 改变画面更新周期

    序号 列名 含义

    • a PID 进程id
    • b PPID 父进程id
    • c RUSER Real user name
    • d UID 进程所有者的用户id
    • e USER 进程所有者的用户名
    • f GROUP 进程所有者的组名
    • g TTY 启动进程的终端名。不是从终端启动的进程则显示为 ?
    • h PR 优先级
    • i NI nice值。负值表示高优先级,正值表示低优先级
    • j P 最后使用的CPU,仅在多CPU环境下有意义
    • k %CPU 上次更新到现在的CPU时间占用百分比
    • l TIME 进程使用的CPU时间总计,单位秒
    • m TIME+ 进程使用的CPU时间总计,单位1/100秒
    • n %MEM 进程使用的物理内存百分比
    • o VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES
    • p SWAP 进程使用的虚拟内存中,被换出的大小,单位kb。
    • q RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA
    • r CODE 可执行代码占用的物理内存大小,单位kb
    • s DATA 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb
    • t SHR 共享内存大小,单位kb
    • u nFLT 页面错误次数
    • v nDRT 最后一次写入到现在,被修改过的页面数。
    • w S 进程状态。(D=不可中断的睡眠状态,R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程)
    • x COMMAND 命令名/命令行
    • y WCHAN 若该进程在睡眠,则显示睡眠中的系统函数名
    • z Flags 任务标志,参考 sched.h

    默认情况下仅显示比较重要的 PID、USER、PR、NI、VIRT、RES、SHR、S、%CPU、%MEM、TIME+、COMMAND 列。可以通过下面的快捷键来更改显示内容。

    通过 f 键可以选择显示的内容。按 f 键之后会显示列的列表,按 a-z 即可显示或隐藏对应的列,最后按回车键确定。
    按 o 键可以改变列的显示顺序。按小写的 a-z 可以将相应的列向右移动,而大写的 A-Z 可以将相应的列向左移动。最后按回车键确定。

    按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序。而大写的 R 键可以将当前的排序倒转。

     

     

    cat /proc/meminfo

      /proc/meminfo是了解Linux系统内存使用状况的主要接口,我们最常用的”free”、”vmstat”等命令就是通过它获取数据的。/proc/meminfo所包含的信息比”free”等命令要丰富得多,但也很复杂,感兴趣的话可以看看这篇文章:http://linuxperf.com/?p=142,对 /proc/meminfo 有较详细的解释。

    $ cat /proc/meminfo
    MemTotal:        8047472 kB
    MemFree:          137996 kB
    MemAvailable:    1775804 kB
    Buffers:          384580 kB
    Cached:          1910796 kB
    SwapCached:        11168 kB
    Active:          5993756 kB
    Inactive:        1423988 kB
    Active(anon):    4886780 kB
    Inactive(anon):   834700 kB
    Active(file):    1106976 kB
    Inactive(file):   589288 kB
    Unevictable:          64 kB
    Mlocked:              64 kB
    SwapTotal:       8267772 kB
    SwapFree:        7929596 kB
    Dirty:               124 kB
    Writeback:             0 kB
    AnonPages:       5115296 kB
    Mapped:           440068 kB
    Shmem:            647576 kB
    Slab:             307660 kB
    SReclaimable:     246212 kB
    SUnreclaim:        61448 kB
    KernelStack:       13136 kB
    PageTables:        69000 kB
    NFS_Unstable:          0 kB
    Bounce:                0 kB
    WritebackTmp:          0 kB
    CommitLimit:    12291508 kB
    Committed_AS:   11107976 kB
    VmallocTotal:   34359738367 kB
    VmallocUsed:           0 kB
    VmallocChunk:          0 kB
    HardwareCorrupted:     0 kB
    AnonHugePages:         0 kB
    ShmemHugePages:        0 kB
    ShmemPmdMapped:        0 kB
    CmaTotal:              0 kB
    CmaFree:               0 kB
    HugePages_Total:       0
    HugePages_Free:        0
    HugePages_Rsvd:        0
    HugePages_Surp:        0
    Hugepagesize:       2048 kB
    DirectMap4k:      270940 kB
    DirectMap2M:     7997440 kB
    DirectMap1G:           0 kB
    

     

    ps aux命令

      ps aux 命令可以查看系统中各个进程的运行情况,包括了进程占用的内存,%MEM 列就是各个进程的内存占用百分比。

     ps -aux
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.0  0.0 185460  4692 ?        Ss   Jun15   0:07 /sbin/init splash
    root         2  0.0  0.0      0     0 ?        S    Jun15   0:00 [kthreadd]
    root         4  0.0  0.0      0     0 ?        I<   Jun15   0:00 [kworker/0:0H]
    root         6  0.0  0.0      0     0 ?        I<   Jun15   0:00 [mm_percpu_wq]
    root         7  0.0  0.0      0     0 ?        S    Jun15   0:03 [ksoftirqd/0]
    root         8  0.1  0.0      0     0 ?        I    Jun15   9:41 [rcu_sched]
    root         9  0.0  0.0      0     0 ?        I    Jun15   0:00 [rcu_bh]
    root        10  0.0  0.0      0     0 ?        S    Jun15   0:00 [migration/0]
    root        11  0.0  0.0      0     0 ?        S    Jun15   0:01 [watchdog/0]
    

     

    展开全文
  • **内存泄漏定义(memory leak):**一个不再被程序使用的对象或变量还在内存中占有存储空间。 一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。 内存溢出 out of memory :指程序申请内存时,...

    **内存泄漏定义(memory leak):**一个不再被程序使用的对象或变量还在内存中占有存储空间。

    一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
    内存溢出 out of memory :指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。
    二者的关系:

    1. 内存泄漏的堆积最终会导致内存溢出
    2. **内存溢出:**就是你要的内存空间超过了系统实际分配给你的空间,此时系统相当于没法满足你的需求,就会报内存溢出的错误。
    3. **内存泄漏 :**是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。就相当于你租了个带钥匙的柜子,你存完东西之后把柜子锁上之后,把钥匙丢了或者没有将钥匙还回去,那么结果就是这个柜子将无法供给任何人使用,也无法被垃圾回收器回收,因为找不到他的任何信息。

    让JVM误以为此对象还在引用中,无法回收,造成内存泄漏:

    1、静态集合类,如HashMap、LinkedList等等。如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。简单而言,长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。

    2、各种连接,如数据库连接、网络连接和IO连接等。在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库的连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection、Statement或ResultSet不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。

    **3、变量不合理的作用域。**一般而言,一个变量的定义的作用范围大于其使用范围,很有可能会造成内存泄漏。另一方面,如果没有及时地把对象设置为null,很有可能导致内存泄漏的发生。

    public class UsingRandom {
    
      private String msg;
    
      public void receiveMsg(){
    
      readFromNet();// 从网络中接受数据保存到msg中
    
      saveDB();// 把msg保存到数据库中
    
      }
    
    }
    

    如上面这个伪代码,通过readFromNet方法把接受的消息保存在变量msg中,然后调用saveDB方法把msg的内容保存到数据库中,此时msg已经就没用了,由于msg的生命周期与对象的生命周期相同,此时msg还不能回收,因此造成了内存泄漏。

    实际上这个msg变量可以放在receiveMsg方法内部,当方法使用完,那么msg的生命周期也就结束,此时就可以回收了。还有一种方法,在使用完msg后,把msg设置为null,这样垃圾回收器也会回收msg的内存空间。

    **4、内部类持有外部类,**如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。

    **5、改变哈希值,**当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露。

    也可以总结为:

    1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
    2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
    3.代码中存在死循环或循环产生过多重复的对象实体;
    4.使用的第三方软件中的BUG;
    5.启动参数内存值设定的过小;

    内存溢出的解决方案:

    第一步,就是修改JVM启动参数,直接增加内存。这一点看上去似乎很简单,但很容易被忽略。JVM默认可以使用的内存为64M,Tomcat默认可以使用的内存为128MB,对于稍复杂一点的系统就会不够用。在某项目中,就因为启动参数使用的默认值,经常报“OutOfMemory”错误。因此,-Xms,-Xmx参数一定不要忘记加。

    **第二步,检查错误日志,**查看“OutOfMemory”错误前是否有其它异常或错误。**在一个项目中,使用两个数据库连接,其中专用于发送短信的数据库连接使用DBCP连接池管理,用户为不将短信发出,有意将数据库连接用户名改错,使得日志中有许多数据库连接异常的日志,一段时间后,就出现“OutOfMemory”错误。经分析,这是由于DBCP连接池BUG引起的,数据库连接不上后,没有将连接释放,最终使得DBCP报“OutOfMemory”错误。经过修改正确数据库连接参数后,就没有再出现内存溢出的错误。

    查看日志对于分析内存溢出是非常重要的,通过仔细查看日志,分析内存溢出前做过哪些操作,可以大致定位有问题的模块。

    **第三步,安排有经验的编程人员对代码进行走查和分析,找出可能发生内存溢出的位置。**重点排查以下几点:

    l 检查代码中是否有死循环或递归调用。

    l 检查是否有大循环重复产生新对象实体。

    l 检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。

    l 检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。

    **第四步,使用内存查看工具动态查看内存使用情况。**某个项目上线后,每次系统启动两天后,就会出现内存溢出的错误。这种情况一般是代码中出现了缓慢的内存泄漏,用上面三个步骤解决不了,这就需要使用内存查看工具了。

    内存查看工具有许多,比较有名的有:Optimizeit Profiler、JProbe Profiler、JinSight和Java1.5的Jconsole等。它们的基本工作原理大同小异,都是监测Java程序运行时所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员可以根据这些信息判断程序是否有内存泄漏问题。一般来说,一个正常的系统在其启动完成后其内存的占用量是基本稳定的,而不应该是无限制的增长的。持续地观察系统运行时使用的内存的大小,可以看到在内存使用监控窗口中是基本规则的锯齿形的图线,如果内存的大小持续地增长,则说明系统存在内存泄漏问题。通过间隔一段时间取一次内存快照,然后对内存快照中对象的使用与引用等信息进行比对与分析,可以找出是哪个类的对象在泄漏。

    如何查看内存溢出:

    1.在linux下首先找到tomcat的PID

    步骤1:ps aux|grep tomcat_1

    步骤2:用jhat生成dump文件,文件后缀为hprof(dump文件后缀的用mat打不开)

    jmap -dump:format=b,file=/opt/tomcat6666.hprof 15837

    下载到windows下:

    sz tomcat6666.hprof

    步骤3:下载MAT

    http://www.eclipse.org/mat/downloads.php

    img

    不需要单独安装eclipse,解压后点击如图exe文件

    image-20201129004638032

    image-20201129004739064

    打开刚才的文件tomcat6666.hprof

    效果图如下:

    image-20201129004812488

    备注:

    linux下执行 jstack 15837 >zxm.txt

    打开zxm.txt,

    如下:

    image-20201129004842268
    如果大家对java架构相关感兴趣,可以关注下面公众号,会持续更新java基础面试题, netty, spring boot,spring cloud等系列文章,一系列干货随时送达, 超神之路从此展开, BTAJ不再是梦想!

    架构殿堂

    展开全文
  • 实时数据库,内存数据库,关系型数据库比较

    万次阅读 多人点赞 2018-09-26 18:55:10
    内存数据库就是将数据放在内存中直接操作的数据库,它利用内存的读写速度比磁盘快、内存是随机访问而磁盘是顺序访问这两个特点,将数据保存在内存中,在内存中模仿建立表结构和索引结构并针对内存特性进行优化,相比...
  • 一文详解堆栈(二)——内存堆与内存

    千次阅读 多人点赞 2019-10-15 19:06:15
    前言:我们经常听见一个概念,堆(heap)和栈(stack),其实在数据结构中也有同样的这两个概念,但是这和内存的堆栈是不一样的东西哦,本文也会说明他们之间的区别的,另外,本文的只是是以C/C++为背景来说明,不同...
  • 首先内存泄漏问题、内存溢出问题可都能会OOM(OutofMemoryError) 堆空间不足 1、内存泄漏问题导致 内存泄漏:是指在堆空间中一直有引用链引用着某些对象。导致对象不能被垃圾收集。 解决办法:如果是内存泄漏,课...
  • 【Linux】Linux的内核空间(低端内存、高端内存

    万次阅读 多人点赞 2018-07-20 16:50:01
    如果用户特意需要从ZONE_DMA或ZONE_HOGNMEM获得内存,那么就需要内存请求者在内存请求函数中使用以下两个区修饰符说明: Linux的主要内核内存分配区修饰符 修饰符 说明 __GFP_DMA 从ZONE_DMA...
  • 面试官:说说什么是 Java 内存模型(JMM)?

    万次阅读 多人点赞 2021-05-05 23:23:20
    3.1. Java 运行时内存区域与硬件内存的关系 3.2. Java 线程与主内存的关系 3.3. 线程间通信 4. 有态度的总结 在面试中,面试官经常喜欢问:『说说什么是Java内存模型(JMM)?』 面试者内心狂喜,这题刚背过:...
  • jemalloc 内存分配器 是什么

    万次阅读 2021-08-04 13:39:40
    所谓内存池,是指应用程序向操作系统(或 JVM)申请一块内存,自己管理这一块内存,对象的创建和销毁都从这块内存中分配和回收,这么一块内存就可以称作内存池, 对应地,管理这块内存的工具就称作内存分配器。 同时...
  • 面试官:怎么做JDK8的内存调优?

    万次阅读 多人点赞 2020-08-03 10:23:15
    擦了擦额头上汗,我稍微调整了一下紧张的情绪,对面试官说: 在内存调优之前,需要先了解JDK8的内存区域是怎么划分的: JDK8内存结构 JDK8的内存结构主要包括程序计数器(Program Counter Register)、虚拟机栈...
  • 转自:https://blog.csdn.net/rebirthme/article/details/50402082想必在linux上写过程序的同学都有分析进程占用多少内存的经历,或者被问到这样的问题——你的程序在运行时占用了多少内存(物理内存)?通常我们可以...
  • Spark内存管理之堆内/堆外内存原理详解

    千次阅读 多人点赞 2019-09-27 10:47:06
    文章目录概要1. 堆内内存(On-heap Memory)1.1 堆内内存分区2.... 内存分配示例5.1 只用了堆内内存5.2 堆内内存+堆外内存总结 概要 介绍Spark内存管理中,涉及到的**堆内内存(On-heap Memory)和堆外内...
  • 查看linux系统中空闲内存/物理内存使用/剩余内存查看系统内存有很多方法,但主要的是用top命令和free 命令当执行top命令看到结果,要怎么看呢?这里说明一下:Mem: 666666k total, 55555k used,并不是代表你的应用...
  • C语言指针与汇编内存地址(二)

    千人学习 2016-05-26 10:37:35
    C语言指针与汇编内存地址视频教程,该课程学习和使用过C语言的程序员都认为要做到对指针这一概念的透彻理解和灵活运用相当困难。本课程会详细讲解C语言与指针、以及如何查看汇编内存等C语言高级教程内容。
  • JavaScript,会在创建变量(对象,字符串等)时分配内存,并且在不再使用它们时“自动”释放内存,这个自动释放内存的过程称为垃圾回收。 因为自动垃圾回收机制的存在,让大多Javascript开发者感觉他们可以不关心...
  • 在清理前内存使用情况 free -m -m是单位,也可以-g 用以下命令清理内存 echo 1 > /proc/sys/vm/drop_caches 清理后内存使用情况再用以下命令看看。 free –m 多出很多内存了吧。 ...
  • 日期 内核版本 架构 作者 GitHub CSDN 2016-08-31 Linux-4.7 X86 & arm ... Linux内存管理 http://blog.csdn.net/vanbreaker/article/details/75799411 前景回顾前面我们讲到服务器体系(SMP, NUMA, M
  • 堆外内存与堆内内存详解

    万次阅读 多人点赞 2018-05-07 17:14:42
    一、什么是堆外内存1、堆内内存(on-heap memory)回顾堆外内存和堆内内存是相对的二个概念,其中堆内内存是我们平常工作中接触比较多的,我们在jvm参数中只要使用-Xms,-Xmx等参数就可以设置堆的大小和最大值,理解...
  • NIO效率高的原理之零拷贝与直接内存映射

    万次阅读 多人点赞 2019-07-31 08:00:00
    首先澄清,零拷贝与内存直接映射并不是Java中独有的概念,并且这两个技术并不是等价的。 零拷贝 零拷贝是指避免在用户态(User-space) 与内核态(Kernel-space) 之间来回拷贝数据的技术。 传统IO 传统IO读取数据并...
  • ???? 作者:Linux猿 ???? 简介:CSDN博客专家?...,C/C++、面试、刷题、算法尽管咨询我,关注我,有...在写 C/C++ 代码的时候,经常需要为程序分配动态内存,难免就会发生内存泄漏的情况,本文就来说一下如何防止 C/C++
  • Android 内存管理机制

    千次阅读 2018-09-03 14:41:45
    本文主要包括三大部分内容: 内存管理基础:从整个计算机领域... Android的内存管理相关知识:Android又不同于Linux,它是一个移动操作系统,因此其内存管理上也有自己的特性,这一部分详细讲述Android的内存管理...
  • Ryzen平台下内存超频与内存时序

    万次阅读 2018-10-24 10:41:40
    最近在AMD平台超内存,顺便学习下内存知识,以下内容转自: 内存的工作原理及时序介绍 #超频甜点系列# RYZEN内存超频、选购二三事 RYZEN 平台超频的一点研究 在此感谢原贴作者们普及知识以及传授经验! 内存...
  • Android Camera内存问题剖析

    万次阅读 2020-08-19 10:00:00
    本文通过一类 Android 机型上相机拍摄过程中的 native 内存 OOM 的问题展开,借助内存快照裁剪回捞和 Native 内存监控工具的赋能,来深入剖析此类问题。背景Rapha...
  • Redis内存优化

    千次阅读 2020-04-16 14:50:06
    使用maxmemory参数限制最大可用内存,当超出内存上限maxmemory时使用LRU等删除策略释放空间以及防止所用内存超过服务器物理内存。 2.配置内存回收策略 Redis所用内存达到maxmemory上限时会触发相应的溢出控制策略...
  • chrome内存泄露(一)、内存泄漏分析工具

    万次阅读 多人点赞 2018-12-02 16:18:27
    内存分析使用的工具包括chrome任务管理器、chrome时间轴(低版本是Timeline,高版本对应performance)、chrome memory(低版本是chrome profiles,主要用JS堆快照、JS堆动态分配时间轴)。文中分析所用chrome版本是...
  • matlab 内存管理 清理内存

    千次阅读 2018-11-14 15:17:00
    一、利用clear清除内存时,要用pack函数进行内存整理 二、查询matlab内存使用,计算要处理的数据大小 三、matlab内存管理 一、利用clear清除内存时,要用pack函数进行内存整理 Matlab在运行大数据时,会出现Out ...
  • 内存分页机制

    万次阅读 多人点赞 2018-09-27 18:08:33
    但仅仅这样还是不够的,如果应用程序过多,或者内存碎片过多,又或者曾经被换出到硬盘的内存段需要再重新装载到内存,可内存中找不到合适大小的区域,要如何解决? 内存置换 当下大部分操作系统的方案是,将一些...
  • 【Linux】Linux的共享内存

    万次阅读 多人点赞 2018-08-10 19:17:45
    实现进程间通信最简单也是最直接的方法就是共享内存——为参与通信的多个进程在内存中开辟一个共享区。由于进程可以直接对共享内存进行读写操作,因此这种通信方式效率特别高,但其弱点是,它没有互斥机制,需要信号...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,776,387
精华内容 1,910,554
关键字:

内存