精华内容
下载资源
问答
  • 对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer), 其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配, ...

    java是面向对象的语言,创建对象的方式有很多种通过关键字new、反射、克隆都可创建对象;比如最常使用的是使用关键字new创建对象,在jvm虚拟机执行到关键字new的时候会去堆内存中检查new出来的类是否已经被加载,如果没有被加载,首先会将类加载;通过检查这个动作之后会给这个new出来的对象分配内存,分配的内存主要用来存放对象的实例变量,
    ,在分配内存的过程中根据对象中实例变量中的具体情况确认需要分配内存空间的大小,然后从java堆内存中分出这样的一块区域用来存储对象的变量(没有被JIT优化的情况下),在 gc回收的过程中根据JVM使用的垃圾回收器的类型,因其回收算法不同,会导致堆中内存分配情况不同。如标记-清除算法回收后的内存中会有大量不连续的内存碎片,在给新的对象分配的时候,就需要通过"空闲列表"来确定一块空闲区域,无论使用哪种方法最终都要确定出一块内存区域,这一块内存区域用来存储创建的对象的引用,然后进行初始化的过程,一个情况下 这样能的分配内存是没有问题的,但是如果在并发的场景中是如何保证分配内存的线程是安全的,如何保不会有多个对象指向同一快内存区域
    一般有两种方式解决:
    1、确保分配内存的线程是安全的,给分配内存的这个操作加锁,对分配内存的动作做同步处理,采用CAS机制,配合失败重试的方式保证更新操作的线程安全性,这种方式可以解决问题,但是在jvm中穿件对象分配内存是一个很频繁的操作在每一次分配内存的动作上都要做安全同步,很显然采用这种方式的效率会很低;
    2、在每个线程上先预先分配出一小块内存,然后该线程在创建对象分配内存的时候,使用预先非配出来的这块 “私有化” 内存,在是“私有化”内存上进行分配,当这个内存用完时在分配一块新的私有化内存;这一种方式称为TLAB(Thread Local Allocation Buffer)分配,这一块Buffer是从堆内存中分配出来的,但是是本地线程共独享的,需要重点关注的是,这一块内存我们说是TLAB独享的,但只是在分配内存的动作是独享的,在读取、使用,垃圾回收的动作上是共享的,并且在使用上和普通的堆存没有区别,另外,TLAB分配的内存的方式只作用在Elden 区,对于新分配的对象会首先分配在这块内存区域,新生代分配不了的大对象会直接分配到老年代,所以在java中多个小对象比一个大对象具有更高的内存分配效率
    所以TLAB分配的内存存放在Eden区,是还是会被垃圾回收或者被移到Survivor 区、Old 区,
    使用了TLAB之后,在TLAB上给对象分配内存时线程独享的了,这就没有冲突了,但是,TLAB这块内存自身从堆中划分出来的过程也可能存在内存安全问题,因此在对于TLAB 分配内存的过程还需要做同步安全的操作,相比于每次为单个对象划分内存时候对进行同步控制的要低的多**
    -XX:+/-UseTLAB参数来指定是否开启TLAB分配
    总结:
    1,堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的
    2,JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer), 其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,
    在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配
    3、TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。
    4 ,所有新创建的Object 都将会存储在新生代Yong Generation中。
    如果Young Generation的数据在一次或多次GC后存活下来,那么将被转移到OldGeneration。 新的Object总是创建在Eden Space。

    展开全文
  • JVM分配内存的两种方式

    千次阅读 2019-05-17 15:44:19
    当使用new关键字创建一个类的对象时,虚拟机需要为新生对象分配内存空间,而对象的大小在类加载完成后已经确定了,所以分配内存只需要在Java堆中划分出一块大小相等的内存。在Java虚拟机中有指针碰撞和空闲列表两种...

    当使用new关键字创建一个类的对象时,虚拟机需要为新生对象分配内存空间,而对象的大小在类加载完成后已经确定了,所以分配内存只需要在Java堆中划分出一块大小相等的内存。在Java虚拟机中有指针碰撞和空闲列表两种方式分配内存。

    1. 指针碰撞方式
    如果Java堆中内存是规整排列的,所有被用过的内存放一边,空闲的可用内存放一边,中间放置一个指针作为它们的分界点,在需要为新生对象分配内存的时候,只要将指针向空闲内存那边挪动一段与对象大小相等的距离即可分配。

    2. 空闲列表方式
    如果Java堆中内存不是规整排列的,用过的内存和可用内存是相互交错的,这种情况下将不能使用指针碰撞方式分配内存,Java虚拟机需要维护一个列表用于记录哪些内存是可用的,在为新生对象分配内存的时候,在列表中寻找一块足够大的内存分配,并更新列表上的记录。
    3. Java虚拟机选择策略
    Java虚拟机采用哪种方式为新生对象分配内存,取决于所使用的垃圾收集器,当垃圾收集器具有整理过程时,虚拟机将采用指针碰撞的方式;当垃圾收集器的回收过程没有整理过程时,则采用空闲列表方式。

    展开全文
  • JVM初始分配内存.doc JVM初始分配内存.doc
  • JVM内存分配策略

    千次阅读 2019-01-23 18:00:34
    对象内存的分配主要是在堆上分配内存,对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程有限分配在TLAB上。少数情况可以直接分配在老年代中。 TLAB技术:JVM在内存新生代Eden Space中开辟了...

    本篇内容来自深入理解java虚拟机_JVM高级特性和最佳实践

    JVM内存分配策略

    java的内存自动管理可以总结为自动分配和自动回收内存。
    对象内存的分配主要是在堆上分配内存,对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程有限分配在TLAB上。少数情况可以直接分配在老年代中。
    TLAB技术:JVM在内存新生代Eden Space中开辟了一小块线程私有的区域,称作TLAB(Thread-local allocation buffer)。Java程序中很多对象使用过一次就会被销毁,这些小对象也不存在线程共享会很快被GC回收,所以对于小对象通常JVM会优先分配在TLAB上,并且TLAB上的分配由于是线程私有所以没有锁开销。
    回收请看:JVM垃圾回收算法
    普遍的几条内存分配规则:

    • 对象优先在Eden上分配
    • 大对象直接进入老年代
    • 长期存活的对象将进入老年代
    • 动态对象年龄判定
    • 空间分配担保

    发生在新生代的GC称为MinorGC,由于新生代对象很多具有使用一次就不再使用的特性,MinorGC会发生的比较频繁,回收速度也会比较快。
    发生在老年代的GC,称为MajorGC或者FullGC,其实两者也稍有不同,但我们一般认为他两个词是一种意思。一般发生MajorGC也会伴随至少一次MinorGC(并非绝对,在Parallel Scavenge 收集器的收集策略里就有直接进行MajorGC的策略选择过程)。MajorGC的速度一般会比MinorGC慢10倍,但是MajorGC发生频率没有MinorGC频繁。

    对象优先分配在Eden上

    一般情况下对象会分配到新生代Eden上,当Eden没有足够空间进行分配时,虚拟机JVM将进行一次MinorGC。

    大对象直接进入老年代

    需要大量连续内存空间的java对象称为大对象例如字符串和数组。大对象对于分配内存来说不是个好消息,如果大对象能够再分配内存后频繁使用那还不算坏,如果是很多只使用一次的大对象,为了能够提供连续的内存空间来存储这些大对象,就需要频繁的进行垃圾回收,所以应减少使用很多只使用一次的大对象。
    为了对大对象的内存分配进行优化,JVM提供了-XX:pretenureSizeThreshold,大于这个数的对象会被直接分配到老年代,可以避免Eden区和两个Survivor区之间大量的对象复制。因为如果不直接分配到老年代中,很多大对象会从Eden复制到Survivor,然后再分配到老年代(如果最终能分配到老年代)。
    注意:-XX:pretenureSizeThreshold只对Serial和ParNew两款垃圾收集器有效。

    长期存活的对象进入老年代

    JVM的分代垃圾收集思想要区分识别哪些对象应该分配到新生代,哪些对象应该分配到老年代,为了实现这个功能,JVM给每个对象定义了一个对象年龄(Age)计数器,如果对象在Eden出生并经过第一次MinorGC后仍然存活,并且Survivor有空间可以存放,这个对象将被移动到Survivor中,并且对象年龄设置为1.对象在Survivor中每经历过一次MinorGC如果仍然存活age增加1,当age增加到一定程度,对象将被放入老年代。
    设置-XX:MaxTenuringThreshold参数可以对age进入老年代的阈值进行设置。

    动态对象年龄判断

    上面说了对象的age要到达XX:MaxTenuringThreshold设置的参数后才能被存放到老年代,但JVM为了适应不同程序的内存状况,它并不是永远的要求对象的年龄必须到XX:MaxTenuringThreshold,如果在Survivor空间中相同年龄所有对象大小的综合大于Survivor空间的一般,年龄大于或者等于该年龄的对象就可以直接进入老年代。

    空间分配担保

    在发生MinorGC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果是,那么MinorGC可以确保是安全的,如果不是JVM会查看HandlePromotionFailure设置值是否允许担保失败,如果允许JVM会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于将尝试一次MinorGC,如果小于或者设置不允许HandlePromotionFailure,这时就会进行一次FullGC。
    为什么要尝试进行一次MinorGC呢?有什么风险?
    新生代使用复制收集算法,但为了提高内存使用率,只使用了一个Survivor作为备份存储内存,当出现当量对象在MinorGC后仍然存活时(极端情况是内存回收后新生代中对象全存活),就需要老年代进行分配担保,把Survivor无法容纳的对象进入到老年代中,老年代要进行担保,前提是老年代本身还有容纳这些对象的剩余空间,一共有多少对象会醋挪下来在实际完成之前无法明确知道,所以只好取之前每一次回收晋升到老年代对象哦让你过量的平均值作为经验值,和老年代中的剩余空间进行比较,决定是否进行FullGC来让老年代腾出更多空间。
    担保是可能失败的,就是这次需要存入的对象远大于预估值,这时担保失败,对象也无法存入老年代,需要重新发起一次FullGC。

    展开全文
  • 内存分配 内存回收 ------------------------------------------------------ 内存回收经常也被叫做垃圾回收。(附带资料:JVM面试题超链接、JVM性能调优和 参数说明) *很多人迷惑一个问题,既然J...

     ---------------------------------------------------

    JVM内存管理分为两部分:

    内存分配

    内存回收

    ------------------------------------------------------

    内存回收经常也被叫做垃圾回收。(附带资料:JVM面试题超链接JVM性能调优 和 参数说明 )

    *很多人迷惑一个问题,既然Java采用自动内存管理,程序员不用关心内存管理的细节,那么为什么我们仍然需要了解Java内存管理的内幕?

    很简单:

    1.了解Java内存管理的细节,有助于程序员编写出性能更好的程序。

       比如,在新的线程创建时,JVM会为每个线程创建一个专属的栈(stack),其栈   是先进后出的数据结构,这种方式的特点,让程序员编程时,必    须特别注意递归方法要尽量少使用,另外栈的大小也有一定的限制,如果过多的递归,容易导致stack overflow。

    2.了解Java内存管理的细节,一旦内存管理出现问题,有助于找到问题的根本原因所在

    3.了解Java内存管理的内幕,有助于优化JVM,使自己的应用获得最好性能体验。

    本节要说的几个主要内容:     内存--- Java中哪些组件用到内存---内存分配机制---内存回收机制

    1 内存

    1.1 物理内存和虚拟内存

    物理内存就是常说的RAM(随机存储器),操作系统作为我们管理计算机物理内存的接口,我们通常都是通过调用计算机操作系统来访问内存的,在Java中,甚至不需要写和内存相关的代码。

    通常操作系统管理内存申请空间是按照进程来管理的,每个进程都有一段独立的空间,互不重合,互不访问。这里所说的内存空间的独立是指逻辑上的独立,由操作系统来保证的。

    虚拟内存的出现使得多个进程可以同时运行时共享物理内存,空间上共享,逻辑仍然互不访问。虚拟内存提高了内存利用率,扩展了内存地址空间。

    1.2 内核空间和用户空间

    通常一个4GB的物理内存地址空间并不能完全被使用,因为它被划分为两部分:内核空间用户空间

      【内核空间】主要是指操作系统运行时用于程序调度、虚拟内存的使用或者连接硬件资源的程序逻辑。

      【用户空间】是用户运行程序能够申请使用的空间。

    为什么这么划分呢?

        1)有效抵御恶意用户的窥探,也能防止质量低劣的用户程序的侵害,从而使系统运行得更稳定可靠

        2)用户空间与内核空间的权限不同,内核空间拥有所有硬件设备的权限,用户空间只有普通硬件的权限;两者隔离可以防止用户程序直接访问硬件资源

         但是,每一次系统调用都会在两个内存空间之间切换,通过网络传输的数据首先被接收到内核空间,然后再从内核空间复制到用户空间供用户使用。这样比较费时,虽然保证了程序运行的安全性和稳定性,但同时也牺牲了一部分效率。(后来出现了一些列优化技术,如Linux提供的sendfile文件传输方式)

    另外:

    Windows32位操作系统【内核空间:用户空间=1:1】

    Linux32位操作系统【内核空间:用户空间=1:3】

    Java中的内存就是从物理内存中申请下来的内存,它怎么被划分的呢!?

    2 Java内存模型

    2.1 Java程序执行流程

    一个Java程序的具体执行流程如下:

                           

           首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行。在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存

    2.2 Java内存划分

    我们常说的Java内存管理就是指这块区域的内存分配回收,那么,这块儿区域具体是怎么划分的呢?

    根据《Java虚拟机规范》的规定,运行时数据区通常包括这几个部分:

    程序计数器(ProgramCounter Register)

    Java栈(VM Stack)

    本地方法栈(Native MethodStack)

    方法区(Method Area)

    堆(Heap)

                        

    Java堆和方法区是所有线程共享(所有执行引擎可访问)

    【Java堆】用于存储Java对象,每个Java对象都是这个对象类的副本,会复制包含继承自它父类的所有非静态属性。

    【方法区】用于存储类结构信息,class文件加载进JVM时会被解析成JVM识别的几个部分分别存储在不同的数据结构中:常量池、域、方法数据、方法体、构造函数,包括类中的方法、实例初始化、接口初始化等。

    方法区被JVM的GC回收器管理,但是比较稳定,并没有那么频繁的被GC回收。

    java栈和PC寄存器(程序计数器)是线程私有,每个执行引擎启动时都会创建自己的java栈和PC寄存器;

     【Java栈】和线程关联,每个线程创建的时候,JVM都会为他分配一个对应的Java栈,这个栈含有多个栈帧;栈帧则是个方法关联,每个方法的运行都会创建一个自己的栈帧,含有内存变量,操作栈、方法返回值。

     (用于存储方法参数、局部变量、方法返回值和运算中间结果)

    【PC寄存器】则用于记录下一条要执行的字节码指令地址和被中断地址。如果方法是 native的,程序计数器的值不会被定义为空。

    【本地方法栈】是为JVM运行Native方法(本地方法:非java语言编写的方法,被编译成和处理器相关的代码)准备的空间,类似于Java栈。

    【运行时常量池】关于这个东西要明白三个概念:

    • 常量池(Constant Pool):常量池数据编译期被确定,是Class文件中的一部分。存储了类、方法、接口等中的常量,当然也包括字符串常量。
    • 字符串池/字符串常量池(String Pool/String Constant Pool):是常量池中的一部分,存储编译期类中产生的字符串类型数据。
    • 运行时常量池(Runtime Constant Pool):方法区的一部分,所有线程共享。虚拟机加载Class后把常量池中的数据放入到运行时常量池。 

    3 Java中哪些组件用到内存

    Java堆

           Java堆用于存储Java对象,在JVM启动时就一次性申请到固定大小的空间,所以,一旦分配,大小不变。

    • 内存空间管理:JVM
    • 对象创建:Java应用程序
    • 对象所占空间释放:垃圾收集器 

    线程

           JVM运行实际程序的实体就是线程,每个线程创建的时候JVM都为它创建了私有的堆栈和程序计数器(或者叫做PC寄存器);很多应用程序是根据CPU的核数来分配创建的线程数。

    类和类加载器

       Java中的类和类加载器同样需要存储空间,被存储在永久代(PermGen区)当中。

       JVM加载类方式:按需加载,只加载那些你在程序中明确使用到的类,通常只加载一次,如果一直重复加载,可能会导致内存泄露,所以也要注意对PernGen区失效类的卸载内存回收问题。

    通常PernGen区满足内存回收的条件为:

    1) 堆中没有对该类加载器的引用;(java.lang.ClassLoader对象)

    2) 堆中没有对类加载器加载的类的引用;(java.lang.Class对象)

    3) 该类加载器加载的类的所有实例化的对象不再存活。

    NIO

         NIO使用java.nio.ByteBuffer.allocateDirect()方法分配内存,每次分配内存都会调用操作系统函数os::malloc(),所以,分配的内存是本机的内存而不是Java堆上的内存;

         另外利用该方法产生的数据和网络、磁盘发生交互的时候都是在内核空间发生的,不需要复制到用户空间Java内存中,这种技术避免了Java堆和本机堆之间的数据复制;但是利用该方法生成的数据会作为Java堆GC的一部分来自动清理本机缓冲区。

    JNI

         JNI技术使本机代码可调用java代码,Java代码的运行本身也依赖于JNI代码来实现类库功能,所以JNI也增加内存占用。

    4 JVM内存分配与回收

    4.1 内存分配

    4.1.1 通常的内存分配策略

    操作系统中内存分配策略通常分为三类:

    【静态内存分配】编译时就分配了固定的内存空间(编译器确定所需空间大小),不允许有可变数据和递归嵌套等情况,这样难以计算具体空间;

    【栈内存分配】程序运行时进入一个程序模块(程序入口处确定空间大小)知道一个程序模块分配所需数据区大小并为之分配内存。

    【堆内存分配】程序运行到相应代码是才会知道所需空间大小。(运行时确定空间大小)

     很明显,三种分配策略中,堆内存分配策略最自由,但是效率也是比较差的。

    4.1.2 Java内存分配详解

           在Java程序运行过程中,JVM定义了各种区域用于存储运行时数据。其中的有些数据区域在JVM启动时创建,并只在JVM退出时销毁;其它的数据区域与每个线程相关。这些数据区域,在线程创建时创建,在线程退出时销毁。

    栈和线程

          JVM是基于栈的虚拟机,为每个新创建的线程都分配一个栈,也就是说一个Java程序来说,它的运行就是通过对栈的操作来完成的。栈以帧为单位保存线程的状态。JVM对栈只进行两种操作:以帧为单位的压栈出栈操作。

         某个线程正在执行的方法称为此线程的当前方法,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的 Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程其他数据。这个帧在这里和编译原理中的活动纪录的概念是差不多的。

         从Java的这种分配机制来看,可以这样理解:栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。

    堆和栈的区别

    1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方 。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
    2. 栈的优势是,存取速度比堆要快 ,仅次于直接位于CPU中的寄存器,缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可 以共享,详见第4点。

        堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

    3.两者存储数据类型不同

        是一个运行时数据区,存放通过new、newayyray.anewarray和mulitanewarray等指令建立的对象,无需代码显式的释放;

        栈中存放一些基本类型的变量数据(int/short/long/byte/float/double/Boolean/char)和对象句柄(引用);  

        Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配;也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)。 

    4.2  JVM内存回收

    4.2.1 几个问题要搞清楚

    问题一:什么叫垃圾回收机制?
          垃圾回收是一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用,以免造成内存泄露

    问题二:java的垃圾回收有什么特点?
          Java语言不允许程序员直接控制内存空间的使用。内存空间的分配和回收都是由JRE负责在后台自动进行的,尤其是无用内存空间的回收操作(garbagecollection,也称垃圾回收),只能由运行环境提供的一个超级线程进行监测和控制

    问题三:垃圾回收器什么时候会运行?
          一般是在CPU空闲或空间不足时自动进行垃圾回收,而程序员无法精确控制垃圾回收的时机和顺序等。、

    问题四:什么样的对象符合垃圾回收条件?
          当没有任何获得线程能访问一个对象时,该对象就符合垃圾回收条件。

    问题五:垃圾回收器是怎样工作的?
          垃圾回收器如发现一个对象不能被任何活线程访问时,他将认为该对象符合删除条件,就将其加入回收队列,但不是立即销毁对象,何时销毁并释放内存是无法预知的。垃圾回收不能强制执行,然而java提供了一些方法(如:System.gc()方法),允许你请求JVM执行垃圾回收,而不是要求,虚拟机会尽其所能满足请求,但是不能保证JVM从内存中删除所有不用的对象。

    问题六:一个java程序能够耗尽内存吗?
          可以。垃圾收集系统尝试在对象不被使用时把他们从内存中删除。然而,如果保持太多活动对象,系统则可能会耗尽内存。垃圾回收器不能保证有足够的内存,只能保证可用内存尽可能的得到高效的管理

    问题七程序中的数据类型不一样存储地方也不一样,原生数据类型存储在java栈中,方法执行结束就会消失;对象类型存储在Java堆中,可以被共享,不一定随着方法执行结束而消失。

    问题八:如何检测垃圾?(垃圾检测机制)

         垃圾收集器的两个任务:正确检测出垃圾对象和释放垃圾对象占用的内存空间,而前者是关键所在。

         垃圾收集器有一个根对象集合,包含的元素:1)方法中局部变量的引用;2)Java操作栈中的对象引用;3)常量池中的对象引用;4)本地方法持有的对象引用;5)类的class对象。

         JVM在垃圾回收的时候会检查堆中的所有对象是否会被根对象直接或间接的引用,能够被根对象到达的叫做活动对象,否则叫做非活动对象可以被回收。

    4.2.2 内存回收- gc原理

            jvm内存回收采用的是基于分代的垃圾收集算法 

          Sun的JVM Generational Collecting(垃圾回收)原理是这样的:把对象分为年青代(Young)、年老代(Tenured)、持久代(Perm),对不同生命周期的对象使用不同的算法。(基于对象生命周期分析)

       【设计思路:把对象按照寿命长短来分组,分为年轻代和年老代,新创建的对象被分在年轻代,如果对象经过几次回收后仍然存活,那么再把这个对象划分到年老代。年老代的收集频度没有那么频繁,这样就减少了每次垃圾收集时所需要的扫描的对象和数量,从而提高垃圾回收效率。

                       

    1.Young(年轻代)

         年轻代分三个区。一个Eden区,两个Survivor区。大部分对象在Eden区中生成。当Eden区时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制年老区(Tenured,需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden和Survivor区复制过来的对象,而复制到年老区的只有从第一个Survivor过来的对象,而且,Survivor区总有一个是空的。

    2.Tenured(年老代)

         年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象;如果Tenured区(old区)也满了,就会触发Full GC回收整个内存

    3.Perm(持久代)

          用于存放类的Class文件或静态文件,如Java类、方法等,垃圾回收是由FullGC触发的。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。

    举个例子

          当在程序中生成对象时,正常对象会在年轻代中分配空间,如果是过大的对象也可能会直接在年老代生成(据观测在运行某程序时候每次会生成一个十兆的空间用收发消息,这部分内存就会直接在年老代分配)。年轻代在空间被分配完的时候就会发起内存回收,大部分内存会被回收,一部分幸存的内存会被拷贝至Survivor的from区,经过多次回收以后如果from区内存也分配完毕,就会也发生内存回收然后将剩余的对象拷贝至to区。等到to区也满的时候,就会再次发生内存回收然后把幸存的对象拷贝至年老区。

          通常我们说的JVM内存回收总是在指堆内存回收确实只有堆中的内容是动态申请分配的,所以以上对象的年轻代和年老代都是指的JVM的Heap空间,而持久代则是之前提到的MethodArea,不属于Heap。 

     关于JVM内存管理我们需要注意的几个地方:

          1、程序中的无用对象、中间对象置为null,可加快内存回收。

          2、对象池技术如果生成的对象是可重用的对象,只是其中的属性不同时,可以考虑采用对象池减少对象的生成。

               如果对象池中有空闲的对象就取出使用,没有则生成新的对象,提高对象复用率。

          3JVM性能调优通过配置JVM的参数来提高垃圾回收的速度,如果在没有出现内存泄露且上面两种办法都不能保证JVM内存回收时,可以考虑采用JVM调优 的方式来解决,不过一定要经过实体机的长期测试,因为不同的参数可能引起不同的效果。如-Xnoclassgc参数等。

    4.2.3 jvm的垃圾回收算法

       Java中,垃圾回收GC,Garbage Collection)的对象是Java堆和方法区(即永久区或持久区)

       垃圾指的是在系统运行过程当中所产生的一些无用的对象,这些对象占据着一定的内存空间,如果长期不被释放,可能导致OOM。后台专门有一个专门用于垃圾回收的线程来进行监控、扫描,自动将一些无用的内存进行释放,这就是垃圾收集的一个基本思想,目的在于防止由程序猿引入的人为的内存泄露。

        现代java虚拟机常用的垃圾回收算法有三种,分别是标记-清除算法复制算法标记-整理算法

    1 标记-清除算法

    (1)概念:

    标记-清除算法是现代垃圾回收算法的思想基础。它将垃圾回收分为两个阶段:标记阶段清除阶段

    标记阶段首先,通过根节点,标记所有从根节点开始的可达对象。未被标记的对象就是未被引用的垃圾对象;

    清除阶段:然后,清除所有未被标记的对象。

                 7de44970-2e02-46a1-a5d0-0663b21906c6

    (2)算法详解

           原理:当中的可用有效内存空间(available memory)被耗尽的时候,就暂停整个程序(也被成为stop the world),然后进行标记和清除两项工作,然后让程序恢复运行。

    • 标记:标记的过程其实就是,遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象。
    • 清除:清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。

    疑问:为什么非要停止程序的运行呢?

    答:不难理解,假设程序与GC线程一起运行,当对象A处于标记阶段,被标记为垃圾对象后,试想此时新new了一个对象B,且对象A可达B。但是由于此时A对象已经标记结束,B对象错过了标记阶段。因此当接下来清除阶段会被,新对象B会随着A被标记被清除掉,变为null,这样就乱套了。如此一来,要想正常清除垃圾资源,GC线程必须要暂停程序。

    (3)标记-清除算法的缺点:

           首先,它的缺点就是效率比较低(递归与全堆对象遍历),暂停程序stop the world的时间比较长。(尤其对于交互式的应用程序来说简直是无法接受。试想一下,如果你玩一个网站,这个网站一个小时就挂五分钟,你还玩吗?)

           第二则是这种方式清理出来的空闲内存不连续,这点不难理解,我们的死亡对象都是随即的出现在内存的各个角落的,现在把它们清除之后,内存的布局自然会乱七八糟。而为了应付这一点,JVM就不得不维持一个内存的空闲列表,这又是一种开销。而且在分配数组对象的时候,寻找连续的内存空间会不太好找。

    2 复制算法(适用于年轻代GC

    (1)概念:内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。

    • 与标记-清除算法相比,复制算法是一种相对高效的回收方法,且内存连续
    • 不适用于存活对象较多的场合,如老年代(复制算法适合做新生代的GC

          ff1e1846-e49c-4663-aee1-7c63628f567c

    (2)优点:实现简单,运行高效,内存连续。每次只要一动指针,就可联系分配内存存放复制过来的对象。

     缺点:空间浪费,只用了一半内存,所以,要想用复制算法,最起码对象的存活率要非常低才行,而且最重要的是要克服50%内存的浪费。

    针对这种缺点,这种算法比较适合,且已经用于年轻代垃圾回收,新生代中的对象98%都是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块比较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是说,每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的空间会被浪费。

    当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖于老年代进行分配担保,所以大对象直接进入老年代。整个过程如下图所示:

                  7e1f6ed2-e0c4-45e4-b7db-b59c28e1ee9c

     上图中,绿色箭头的位置代表的是大对象,大对象直接进入老年代。

    3 标记-整理算法(适用于年老代的GC)

    (1)引入

        如果在对象存活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选中这种算法。

    (2)概念: 适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从节点开始,对所有可达对象做一次标记;但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端之后,清理边界外所有的空间。

                      cc79889a-0856-4018-92c3-c51108c9caea

    • 标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记
    • 整理:移动所有存活的对象,按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。(JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销)

    (3)

    优点:标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。

    缺点:就是效率也不高。不仅要标记所有存活对象,还要整理所有存活对象的引用地址。从效率上来说,要低于复制算法。

     

    4 标记-清除算法、复制算法、标记整理算法的总结

        三个算法都基于根搜索算法去判断一个对象是否应该被回收,而支撑根搜索算法可以正常工作的理论依据,就是语法中变量作用域的相关内容。因此,要想防止内存泄露,最根本的办法就是掌握好变量作用域,而不应该使用C/C++式内存管理方式。

       在GC线程开启时,或者说GC过程开始时,它们都要暂停应用程序(stop the world)。

    它们的区别如下:

    (1)效率复制算法 > 标记/整理算法 > 标记/清除算法

    (2)内存整齐度复制算法=标记/整理算法>标记/清除算法

    (3)内存利用率:标记/整理算法=标记/清除算法>复制算法

        注1:可以看到标记/清除算法是比较落后的算法了,但是后两种算法却是在此基础上建立的。

        注2:时间与空间不可兼得。

    展开全文
  • 查看tomcat为jvm分配内存大小

    千次阅读 2013-11-07 13:53:28
    查看tomcat为jvm分配内存大小
  • JVM-内存分配与回收策略

    千次阅读 2016-02-24 21:44:37
    回收部分通过之前的《GC设计思路分析》和《垃圾收集器》这两篇博文进行了总结,那么接下来主要就是谈谈自己对JVM是如何给对象分配内存这一部分的理解。JVM的内存空间是有限的,并且堆内存是共享的,那么不同线程共用...
  • JVM内存分配原理

    千次阅读 2015-09-20 10:28:51
    分配前的加载 当使用new关键字创建一个JAVA对象时,JVM首先会检查这个New指令的参数是否在常量池中定位到一个类的符号引用,然后检查与这个符号引用相对应的类...分配内存 基于分代的概念,Java的堆区还可以划分为
  • JVM内存结构和内存分配

    千次阅读 2016-07-14 10:29:46
    JVM内存结构和内存分配
  • JVM运行内存分配和回收

    千次阅读 2018-09-11 09:16:20
    本文来自网易云社区作者:吕宗胜Java语言与C语言相比,最大的特点是编程人员无需过多的关心Java的内存分配和回收,...所以对于Java程序员来说认识和了解JVM内存分配和回收对于代码的编写和应用的优化都有非常重要...
  • jvm内存分配过程

    万次阅读 2017-07-09 20:32:40
    jvm内存分配过程 1、JVM 会试图为相关Java对象在Eden Space中初始化一块内存区域。 2、当Eden空间足够时,内存申请结束;否则到下一步。 3、JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收)。...
  • JVM内存区域分配

    千次阅读 2016-03-20 17:06:21
    JVM内存模型,对象的分配
  • JVM内存结构分配

    千次阅读 2016-03-30 17:16:33
    JVM的堆内存分为新生代(Young Generation)和旧生代(Old Generation)。新生代分为Eden区和Survivor区。Survivor区分为From Survivor和To Survivor。如图: 从上图可以看出,新生代通常占JVM内存的1/3,...
  • JAVA对象在JVM内存分配

    千次阅读 2017-04-22 13:50:07
    如果你还不了解JVM内存模型的建议您先看下JVM内存模型以一下代码为例,来分析下,java对象在内存中的空间分配。public class Student { private String name; private static Birthday birthday = new Birthday(); ...
  • JVM内存分配

    千次阅读 2009-12-02 13:30:00
    如果对JVM内存分配不合理,应用程序运行中常常用抛出java.lang.OutOfMemory和java.lang.OutOfMemoryError: PermGen space异常。所以我们要对JVM调优以避免出现此类问题。JVM内存分配主要通过这几个参数设定:-...
  • JVM最大分配内存

    千次阅读 2011-08-27 16:28:59
    分析了当前比较流行的几个不同公司不同版本JVM的最大内存,得出来的结果如下: 公司 JVM版本 最大内存(兆)client 最大内存(兆)server SUN 1.5.x 1492 1520 SUN 1.5.5(Linux) 2634 2660 SUN 1
  • JVM内存空间分配笔记

    2018-10-24 13:32:50
    主要是JVM内存分配及简单的JVM性能调优
  • 深入理解Java之JVM内存分配

    万次阅读 2017-04-20 22:16:56
    Java堆是被所有线程共享的一块内存区域,所有对象和数组都在堆上进行内存分配。为了进行高效的垃圾回收,虚拟机把堆内存划分成新生代、老年代和永久代(1.8中无永久代,使用metaspace实现)三块区域。 Java把内存...
  • JVM内存分配

    2018-08-17 22:49:35
    内存分配 -Xss 配置线程的最大栈空间 这个参数,决定了函数可调用的最大深度 package com.bjsxt.base001; public class Test04 { //-Xss1m //-Xss5m //栈调用深度 private static int count; ...
  • JVM内存分配与管理详解

    万次阅读 2018-01-23 16:17:58
    了解C++的程序员都知道,在内存管理领域,都是由程序员维护与管理,程序员用于最高的管理权限,但对于java程序员来说,在内存管理领域,程序员不必去关心内存分配以及回收,在jvm自动内存管理机制的帮助下,不需要...
  • JVM内存结构详解

    万次阅读 多人点赞 2019-10-18 12:49:05
    对于开发人员来说,如果不了解Java的JVM,那真的是很难写得一手好代码,很难查得一手好bug。同时,JVM也是面试环节的中重灾区。...下面,开启我们的第一篇文章《JVM内存结构详解》。 学习也是要讲究方式方法...
  • JVM内存分配担保机制

    万次阅读 多人点赞 2018-09-01 23:42:24
    JVM内存分配担保机制  转自:https://cloud.tencent.com/developer/article/1082730   在现实社会中,借款会指定担保人,就是当借款人还不起钱,就由担保人来还钱。 在JVM内存分配...
  • jvm8内存模型和内存分配

    千次阅读 2019-05-09 13:42:00
    ①加载class文件 ②管理并分配内存 ③执行垃圾收集  内存结构图:    栈  java栈中,存放一个个栈帧,每一个栈帧对应一个被调用方法,在栈帧中包含局部变量表、操作数栈、指向运行时常量池的...
  • 一文了解JVM内存分配

    千次阅读 2019-01-05 12:11:55
    关于JVM内存分配一直有想法想自己整理一篇文档,之前总是查询别的博客,对于概念的理解和系统的知识梳理一直没有仔细整理过。所以整理这样一篇文章,夯实基础,后续会查漏补缺,也希望多多指正。 二、概述 ...
  • 主要介绍了JVM内存分配及String常用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 探索JVM 内存分配

    千次阅读 2011-07-15 14:55:10
    Java中的堆内存与栈内存: ...栈内存主要用于Java 代码的加载和执行,一个典型的例子是递归次数过多,JVM...Runtime错误StackOverflowError,栈内存分配与管理完全依赖于JVM本身。   内存可以分为大致分为:
  • JVM 内存分配详解

    千次阅读 2013-11-12 13:24:52
    使用Java程序从数据库中查询大量的数据时出现异常:  java.lang.OutOfMemoryError: ...JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值,其初始空间(即-X
  • JVM之堆内存分配

    千次阅读 2017-06-05 23:49:20
    JVM之堆内存分配
  • JVM内存分配以及存储总结

    千次阅读 2016-04-17 13:12:49
    最近看了一下JVM内存分配,还是比较复杂的。这里做个总结,首先一个common sense就是操作系统会为每个java进程实例化一个jvm实例。jvm然后再来运行java程序,具体的过程就不多说了,简单来说就是核心classloader如...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 241,437
精华内容 96,574
关键字:

jvm分配内存