精华内容
下载资源
问答
  • Java中内存分配策略及堆和栈的比较

    千次阅读 2019-02-26 18:56:37
    Java中内存分配策略及堆和栈的比较   内存分配策略  按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.   静态存储分配是指在编译时就能确定每个数据目标在运行时刻的...

    Java中内存分配策略及堆和栈的比较 

    •  内存分配策略

      按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 

      静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求. 

      栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。 

      静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放. 

    • 堆和栈的比较

      上面的定义从编译原理的教材中总结而来,除静态存储分配之外,都显得很呆板和难以理解,下面撇开静态存储分配,集中比较堆和栈: 

      从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的: 

      在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个"大小多少"是在编译时确定的,不是在运行时. 

      堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中,要求创建一个对象时,只需用 new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存.当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!这也正是导致我们刚才所说的效率低的原因,看来列宁同志说的好,人的优点往往也是人的缺点,人的缺点往往也是人的优点(晕~). 

    • JVM中的堆和栈

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

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

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

      每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。 

    • Java 中的堆和栈

      Java把内存划分成两种:一种是栈内存,一种是堆内存。 

      在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。 

      当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 

      堆内存用来存放由new创建的对象和数组。 

      在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 

      在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。 

      引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。 

      具体的说: 

      栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 

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

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

      栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义: 

      int a = 3; 

      int b = 3; 

      编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。

    展开全文
  • java中内存分配以及static的用法

    千次阅读 2016-07-16 13:49:27
    Java内存分配与管理是Java的核心技术之一,一般Java在内存分配时会涉及到以下区域:1.栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构的栈。 2.堆区:由程序员分配...

    Java内存分配与管理是Java的核心技术之一,一般Java在内存分配时会涉及到以下区域:

    1.栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
    2.堆区:由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
    3.全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
    4.文字常量区:常量字符串就是放在这里的,程序结束后由系统释放 。
    5.程序代码区 :见名思意:存放代码的区域。

    堆栈内存的分配

    栈区:局部变量存在栈内存中。函数的参数值也存放着栈区。保存类的实例,即堆区对象的引用(指针)。
    堆:用来存放动态产生的数据,比如new出来的对象。注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。常量池存在于堆内存。
    代码:package string;

    public class memonry {

    /**
     * @param args
     * a1和a2是局部变量,放在栈区,而new出的对象放在堆区,堆区还有某类的成员变量
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
            a a1=new a();
            a1.a=1;
            a1.b="asd";
            a a2=new a();
            a2.a=2;
            a2.b="a2";
    }
    

    }
    class a{
    int a;
    String b;
    }这里写图片描述

    如果另a2=a1;则图就变成如下所示这里写图片描述
    原因:a2=a1将a1指向堆的地址给了a2,即a2也指向了a1的堆地址。这样原来的那个a2指向的内存就变成java中所谓的垃圾了,就会被jvm垃圾回收机制所回收。

    static的用法:

    静态static:静态会在堆内存创建一个静态内存区。静态有:静态属性,静态方法,静态类。而静态方法不能调用非静态数据,
    因为静态属性依赖于类,而不是对象。而非静态依赖于对象,静态类一旦创建,只会有一个,并且在该类中是共享的。一但使用
    静态属性,那么就会自动赋一个值,比如int型的话默认就为0,boolean的就为false,string就为null。如:某个类中有静态属
    性时,不应该new一个对象去操作该属性,而是应该直接使用该类名去操作他。
    使用static的优点:
    1.一个静态属性在堆内存只能存在一个,即大家可以共享该属性值。
    2.如果一个对象数组中其中的某人属性大家都是相同的,即可以使用静态,这样可以节省内存空间。
    3.静态一旦创建,生命周期就会从创建开始到该程序结束才会死亡。
    4.静态依赖于类,而不是对象。
    代码:
    public class Static {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
            c.number=12;//通过类名直接更改静态属性,
            c c1=new c();
            //c.number=11;//这样也可以,但会给人错觉,认为是对象去操作该静态类,所以不提倡这种写法。
            c1.a();
            c.b();
            c.c=122;
            c.b();
    }
    

    }
    class c{
    public static int c;
    static boolean a;
    static int number;
    String name;
    public void a(){
    System.out.println(number);
    System.out.println(c);

    }
    public static void b(){
        //System.out.println(name);//因为name为非静态属性,而该方法为静态方法,所以无法调用该属性。
        System.out.println(a);
        System.out.println(c);
    }
    

    }

    展开全文
  • Java中对于垃圾回收的策略有多种,而目前商业虚拟机的垃圾收集都采用“分代收集”,这种算法是根据对象存活周期的不同将内存分为几块,一般是将Java堆分为新生代和老年代,根据各个年代的特点采用较适合的收集算法。...

           Java中对于垃圾回收的策略有多种,而目前商业虚拟机的垃圾收集都采用“分代收集”,这种算法是根据对象存活周期的不同将内存分为几块,一般是将Java堆分为新生代和老年代,根据各个年代的特点采用较适合的收集算法。

           新生代中采用的收集算法:复制算法。算法的思想是将可用内存分为大小相等的两块,每次使用其中一块,当一块内存用完了,就将还存活的对象分到另一块。然后把使用过的内存空间一次进行清理。这种算法缺点会明显,就是会浪费一半的空间。而根据IBM研究表明,新生代中98%的对象朝生夕死,所以不需要按照1:1进行分配,而是按照内存分为一块较大的Eden空间和两块较小的Survivor空间(from和to两个),每次使用Eden和其中一个Survivor。当回收时,将Eden和其中一块Survivor中还存活着的对象一次性复制到另一块Survivor中,然后清理Eden和刚才用过的Survivor空间。一般默认的比例为:Eden:from:to=8:1:1。注:可通过-XX:SurvivorRatio=i来设置,默认i=8。当Survivor空间不够时,需要依赖其他内存(老年代)进行分配担保。内存的分配担保:如果另一块Survivor空间没有足够的内存空间存放上一次新生代收集下来的存活对象,那么这些对象将直接通过分配担保机制进入老年代

           老年代中采用的收集算法:标记-整理算法。算法思想:首先标记需要回收的对象,然后让所有存活的对象向一端移动,然后直接清理掉边界以外的内存。

           内存分配及回收策略

           1、对象优先在Eden分配:大多数情况下,对象在新生区Eden区中分配,当Eden没有足够空间分配时,虚拟机将发起一次Minor GC(见以下代码注释),GC后将已有对象放入Survivor中,若Survivor空间不足,则通过分配担保机制提前转移到老年代。

    public class TestMinorGc {
    
    	private static final int _1M=1024*1024;
    	
    	/**
    	 * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGcDetails -XX:SurvivorRatio=8
    	 * 
    	 * 上述在参数在运行程序时配置。-Xms20M -Xmx20M设置堆大小为20M   -Xmn10M:堆中新生代10M。
    	 * 
    	 * -XX:SurvivorRatio=8:Eden占8M。-XX:PrintGcDetails:这个参数告诉虚拟机在发生垃圾
    	 * 
    	 *  收集行为时打印内存回收日志。
    	 * 
    	 * */
    	
    	public static void testAllocation(){
    		byte[] a1,a2,a3,a4;
    		
    		a1=new byte[2*_1M];
    		a2=new byte[2*_1M];
    		a3=new byte[2*_1M];
    		/**   在Eden中为a4分配内存时,发现只剩下2M,内存不够,这时会发生一次Minor GC(Minor GC指发生在新生代的
    		 *    垃圾收集动作,频率较高。引申:老年代GC(Major GC/Full Gc):发生在老年代的GC,速度慢,一般是Minor
    		 *    的1/10.)GC后发现已有的3个2M的对象无法放入Survivor中(空间不够),因此需要使用分配担保机制将它们提前
    		 *    转移到老年代中。
    		 * */
    		a4=new byte[4*_1M];   
    	}
    	
    }

          2、大对象直接进入老年代。所谓大对象是指需要大量连续内存空间的Java对象,例如较长的字符串和较长的数组。虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个值的参数直接在老年代分配。

    public class BigObjectOld {
    
    	private static final int _1M = 1024 * 1024;
    
    	/**
    	 * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGcDetails
    	 * -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728
    	 * 
    	 * 注意:-XX:PretenureSizeThreshold后面的参数不能写3M,而要写3145728。
    	 */
    	public static void testBigObjectOld(){
    		byte[] a;
    		/**
    		 *  a所占内存大于设置的参数,故直接进入老年代。
    		 * */
    		a=new byte[4*_1M];
    	}
    
    }

            3、长期存活的对象进入老年代。虚拟机既然采用了分代收集的思想来管理内存,那么内存回收时就必须能识别对象放在新生代,哪些对象放在老年代。为了做到这点,虚拟机给每个对象定义一个对象年龄计数器。如果对象在Eden出生并经过第一次MinorGC然后仍然存活,并且能被Survivor容纳的话,将被移到Survivor中,并且对象年龄设为1。对象在Survivor中没过一次Minor GC,年龄就增加一岁。当年龄增加到一定程度,就会晋升到老年代。对象晋升老年代的年龄阈值,可以通过参数:-XX:MaxTenuringThreshold设置。

            4、动态对象年龄判定。为了能更好的适应不同程序的内存状况,虚拟机并不是永远的要求对象必须达到MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需达到MaxTenuringThreshold的要求。

           5、空间分配担保。在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果这个条件成立,那么Minor GC可以确保是安全的。若不成立,则虚拟机会查看HandlePromotionFailure设置值是否担保失败,若允许,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小(相当于一个经验值,不保证可以成功),如果大于,,尽管这次Minor GC冒险,也会尝试进行一次Minor GC;如果小于,或者HandlePromotionFailure设置为不允许冒险,那么需要进行一次Full GC。

    展开全文
  • Java 虚拟机内存分配机制

    千次阅读 2017-09-15 15:34:45
    Java 虚拟机内存分配机制 内存区域划分 对于大多数的程序员来说,Java 内存比较流行的说法便是堆和栈,这其实是非常粗略的一种划分,这种划分的“堆”对应内存模型的 Java 堆,“栈”是指虚拟机栈,然而 Java ...

    Java 虚拟机内存分配机制

    内存区域划分

    对于大多数的程序员来说,Java 内存比较流行的说法便是堆和栈,这其实是非常粗略的一种划分,这种划分的“堆”对应内存模型的 Java 堆,“栈”是指虚拟机栈,然而 Java 内存模型远比这更复杂,想深入了解 Java 的内存,还是有必要明白整个内存区域分。

    了解 Java GC 机制,必须先清楚在 JVM 中内存区域的划分。 在 Java 运行时的数据区里,由 JVM 管理的内存区域分为下图几个模块:

    JVM 内存划分

    程序计数器(Program Counter Register)

    程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。 字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。

    每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的。

    如果程序执行的是一个 Java 方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地( native,由 C 语言编写完成)方法,则计数器的值为 Undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区域中唯一一个没有定义 OutOfMemoryError 的区域。

    虚拟机栈(JVM Stack)

    一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作数栈、动态链接、方法出口等,当方法被调用时,栈帧在 JVM 栈中入栈,当方法执行完成时,栈帧出栈。

    局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。 在局部变量表中,只有 long 和 double 类型会占用 2 个局部变量空间(Slot,对于32位机器,一个 Slot 就是 32 个 bit),其它都是 1 个 Slot。 需要注意的是,局部变量表是在编译时就已经确定好的,方法运行所需要分配的空间在栈帧中是完全确定的,在方法的生命周期内都不会改变。

    虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出 StatckOverFlowError(栈溢出);不过多数 Java 虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,直到内存不足,此时,会抛出 OutOfMemoryError(内存溢出)。

    每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。

    本地方法栈(Native Method Statck)

    本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行 Java 方法的,而本地方法栈是用来执行 native 方法的,在很多虚拟机中(如:Sun 的 JDK 默认的 HotSpot 虚拟机),会将本地方法栈与虚拟机栈放在一起使用。

    本地方法栈也是线程私有的。

    堆区(Heap)

    堆区是理解 Java GC 机制最重要的区域,没有之一。 在 JVM 所管理的内存中,堆区是最大的一块,堆区也是 Java GC 机制所管理的主要内存区域,堆区由所有线程共享,在虚拟机启动时创建。 堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对的,也有栈上直接分配的)。

    一般的,根据 Java 虚拟机规范规定,堆内存需要在逻辑上是连续的(在物理上不需要),在实现时,可以是固定大小的,也可以是可扩展的,目前主流的虚拟机都是可扩展的。 如果在执行垃圾回收之后,仍没有足够的内存分配,也不能再扩展,将会抛出 OutOfMemoryError:Java heap space 异常。

    关于堆区的内容还有很多,将在下面“内存分配机制”中详细介绍。

    方法区(Method Area)

    在 Java 虚拟机规范中,将方法区作为堆的一个逻辑部分来对待,但事实上,方法区并不是堆(Non-Heap);另外,不少人的博客中,将 Java GC 的分代收集机制分为 3 个代:青年代,老年代,永久代,这些作者将方法区定义为“永久代”,这是因为,对于之前的 HotSpot Java 虚拟机的实现方式中,将分代收集的思想扩展到了方法区,并将方法区设计成了永久代。 不过,除 HotSpot 之外的多数虚拟机,并不将方法区当做永久代,HotSpot 本身,也计划取消永久代。 本文中,由于主要使用 Oracle JDK6.0,因此仍将使用永久代一词。

    方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final 常量、静态变量、编译器即时编译的代码等。

    方法区在物理上也不需要是连续的,可以选择固定大小或可扩展大小,并且方法区比堆还多了一个限制:可以选择是否执行垃圾收集。 一般的,方法区上执行的垃圾收集是很少的,这也是方法区被称为永久代的原因之一(HotSpot),但这也不代表着在方法区上完全没有垃圾收集,其上的垃圾收集主要是针对常量池的内存回收和对已加载类的卸载。

    在方法区上进行垃圾收集,条件苛刻而且相当困难,效果也不令人满意,所以一般不做太多考虑,可以留作以后进一步深入研究时使用。

    在方法区上定义了 OutOfMemoryError:PermGen space 异常,在内存不足时抛出。

    • 运行时常量池(Runtime Constant Pool)

    方法区的一部分,用于存储编译期就生成的字面常量、符号引用、翻译出来的直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址,将在类链接阶段完成翻译);运行时常量池除了存储编译期常量外,也可以存储在运行时间产生的常量(比如 String 类的 intern() 方法,作用是 String 维护了一个常量池,如果调用的字符 “abc” 已经在常量池中,则返回池中的字符串地址,否则,新建一个常量加入池中,并返回地址)。

    直接内存(Direct Memory)

    直接内存并不是 JVM 管理的内存,可以这样理解,直接内存,就是 JVM 以外的机器内存。

    比如:你有 4G 的内存,JVM占用了1G,则其余的 3G 就是直接内存,JDK 中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配方式,将由 C 语言实现的 native 函数库分配在直接内存中,用存储在 JVM 堆中的 DirectByteBuffer 来引用。 由于直接内存收到本机器内存的限制,所以也可能出现 OutOfMemoryError 的异常。

    内存分配机制

    以下面代码为例,来分析,Java 的实例对象在内存中的空间分配。

    //JVM 启动时将 Person.class 放入方法区
    public class Person {
    
    	//new Person 创建实例后,name 引用放入堆区,name 对象放入常量池
        private String name;
    
    	//new Person 创建实例后,age = 0 放入堆区
        private int age;
    
    	//Person 方法放入方法区,方法内代码作为 Code 属性放入方法区
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
    	//toString 方法放入方法区,方法内代码作为 Code 属性放入方法区
        @Override
        public String toString() {
            return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
        }
    }
    
    //JVM 启动时将 Test.class 放入方法区
    public class Test {
    
    	//main 方法放入方法区,方法内代码作为 Code 属性放入方法区
        public static void main(String[] args) {
    
            //person1 是引用放入虚拟机栈区,new 关键字开辟堆内存 Person 自定义对象放入堆区
            Person person1 = new Person("张三", 18);
            Person person2 = new Person("李四", 20);
    
            //通过 person 引用创建 toString() 方法栈帧
            person1.toString();
            person2.toString();
        }
    }
    
    1. 首先 JVM 会将 Test.class, Person.class 加载到方法区,找到有 main() 方法的类开始执行。

    这里写图片描述

    如上图所示,JVM 找到 main() 方法入口,创建 main() 方法的栈帧放入虚拟机栈,开始执行 main() 方法。

    Person person1 = new Person("张三", 18);
    

    执行到这句代码时,JVM 会先创建 Person

    实例放入堆区,person2 也同理。

    1. 创建完 Person 两个实例,main() 方法中的 person1,person2 会指向堆区中的 0x001,0x002(这里的内存地址仅作为示范)。紧接着会调用 Person 的构造函数进行赋值,如下图:

    这里写图片描述

    如上图所示,新创建的的 Person 实例中的 name, age 开始都是默认值。 调用构造函数之后进行赋值,name 是 String 引用类型,会在常量池中创建并将地址赋值给 name,age 是基本数据类型将直接保存数值。

    注:Java 中基本类型的包装类的大部分都实现了常量池技术,这些类是 Byte, Short, Integer, Long, Character, Boolean,另外两种浮点数类型的包装类则没有实现。

    基本数据类型包装类 (是否实现了常量池技术)
    byteByte 是
    booleanBoolean 是
    shortShort 是
    charCharacter 是
    intInteger 是
    longLong 是
    floatFloat 否
    doubleDouble 否
    1. Person 实例初始化完后,执行到 toString() 方法,同 main() 方法一样 JVM 会创建一个 toString() 的栈帧放入虚拟机栈中,执行完之后返回一个值。

    这里写图片描述

    参考资料

    《深入理解 Java 虚拟机》

    更多文章

    https://github.com/jeanboydev/Android-ReadTheFuckingSourceCode

    我的公众号

    欢迎你「扫一扫」下面的二维码,关注我的公众号,可以接受最新的文章推送,有丰厚的抽奖活动和福利等着你哦!?

    如果你有什么疑问或者问题,可以 点击这里 提交 issue,也可以发邮件给我 jeanboy@foxmail.com

    同时欢迎你 Android技术进阶:386463747 来一起交流学习,群里有很多大牛和学习资料,相信一定能帮助到你!

    展开全文
  • Java 堆栈内存分配

    千次阅读 2017-10-12 16:37:24
    但是很难解释什么是程序的堆内存以及栈内存一: Java内存空间Java程序运行时使用java Heap 内存为对象以及JRE类分配内存, 不论我们在何时创建何种类型的对象, 他总是在堆内存中创建的Java 垃圾收集器运行在堆...
  • java数组内存分配

    千次阅读 2020-07-04 17:40:39
    内存中存放局部变量,定义在方法的变量,如arr。使用完毕立即消失。 堆内存存放new出来的内容(实体、对象),每一个new出来的内容都有地址值。使用完毕会在垃圾回收机制空闲时被回收。 数组在初始化时会为存储...
  • Java内存分配和管理

    千次阅读 2018-01-14 22:46:13
    Java内存分配时涉及的区域: 寄存器:在程序无法控制; 栈:存放基本类型的数据和对象的引用,但是对象本身不存放在栈,而是存放在堆; 堆:存放用new产生的数据; 静态域:存放在对象中用static定义的...
  • java new 内存分配

    千次阅读 2017-05-16 14:40:41
    在函数定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后(比如,在函数A调用函数B,在函数B...
  • Java中ArrayList内存分配问题

    千次阅读 2015-10-08 21:42:56
    在深入学习Java语言时,当了解到对象在内存的分配情况时,于是对动态数组ArrayList等集合类是如何分配...原文请参见[JAVA里,ArrayList在内存分配上究竟是怎样的?(http://zhidao.baidu.com/link?url=xDZd1EM9S95C
  • 图解Java继承内存分配

    千次阅读 多人点赞 2013-05-04 10:20:41
    图解Java继承内存分配 继承的基本概念: (1)Java不支持多继承,也就是说子类至多只能有一个父类。 (2)子类继承了其父类不是私有的成员变量和成员方法,作为自己的成员变量和方法。 (3)子类...
  • Java中变量的内存分配

    千次阅读 2016-05-30 11:20:00
    Java内存管理分为两个方面:内存分配 内存回收 这里的内存分配特指当创建一个java对象时JVM为该对象在堆内存分配内存空间。 内存回收:当java对象失去引用,变成垃圾时,  JVM的垃圾回收机制会(1)自动清理该...
  • Java中对象的内存分配机制

    万次阅读 2020-05-02 14:51:38
    当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用后,Java会自动释放掉为该变量所分配内存空间,该内存空间可以立即被另作他用。 2、堆内存内存用来存放由new创建的对象...
  • java数组内存分配内存结构详解

    万次阅读 多人点赞 2018-08-12 19:51:49
    Java 数组是静态的 Java 语言是典型的静态语言,因此 Java 数组是静态的,即当数组被初始化之后...所谓初始化,即创建实际的数组对象,也就是在内存中为数组对象分配内存空间,并为每个数组 元素指定初始值。 数...
  • Java中堆内存与栈内存分配浅析

    千次阅读 2013-09-24 11:01:15
    在函数定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配内存...
  • java虚拟机内存分配原理概述

    千次阅读 2018-08-14 18:08:16
    本文主要介绍在应用发起内存申请,到操作系统最终分配内存,采用了那些途径和方法,并比较各种方法的优劣以及使用过程应该注意那些点。...5、堆和堆内存分配 1、应用发起请求,要求系统分配内存 (...
  • Java中变量内存分配机制

    千次阅读 2013-10-31 21:24:30
    ----------------------------...在本文,简单的为大家介绍有关,java中变量在内存是如何分配的。  在任何编程语言,无论是基本类型还是引用类型,不论其作用域如何,都必须为其分配一定的内存空间,Java
  • java对象内存分配与回收策略

    千次阅读 2020-07-05 16:25:05
    java对象内存的分配,从概念上...使用HotSpot虚拟机,以客户端模式运行,使用Serial+Serial Old客户端默认收集器组合的内存分配和回收策略,最基本的几条内存分配原则。 1. 对象优先在Eden分配 大多数情况下,对象在
  • java中static 内存分配

    千次阅读 2016-10-12 16:33:33
    一个类的静态方法和静态变量使用原理,JVM会把...大家都知道,在程序任何变量或者代码都是在运行时由系统自动分配内存来存储的,而所谓静态就是指在第一次分配内存后,所分配内存会一直存在,直到程序退出内存才会
  • Java直接内存分配与释放原理

    千次阅读 2019-04-02 11:15:54
    Java中分配直接内存大有如下三种主要方式: 1.Unsafe.allocateMemory() 2.ByteBuffer.allocateDirect() 3.native方法 Unsafe类 Java提供了Unsafe类用来进行直接内存分配与释放 public native long allocate...
  • Java虚拟机内存分配机制与启动参数说明,配置堆区:-Xms 、-Xmx、-XX:newSize、-XX:MaxnewSize、-Xmn; 配置非堆区:-XX:PermSize、-XX:MaxPermSize,
  • Java内存管理-内存分配与回收

    千次阅读 2017-12-29 17:48:00
    Java内存管理-内存分配与回收
  • Java对象与内存分配

    千次阅读 2017-11-07 16:39:37
    Java对象与其内存分配Java中一切都是对象,对象是Java运行的单元,知道对象是如何存在的、什么时候是可见的,才知道怎样运用对象来完成相应的操作。 Java运行时对象和变量等都是在内存,可以根据内存的数据来...
  • java运行内存分配

    万次阅读 2012-02-22 12:24:43
    Java内存分配  Java程序运行时的内存结构分成:方法区、栈内存、堆内存、本地方法栈几种。   方法区  存放装载的类数据信息,包括:基本信息:每个类的全限定名、每个类的直接超类的全限定名、该类是类还是接口...
  • 大对象指需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组。 3、长期存活的对象将进入老年代。 虚拟机采用分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代...
  • Java数组及其内存分配

    千次阅读 2016-08-30 10:22:25
    几乎所有的程序设计语言都支持数组。Java也不例外。当我们需要多个类型相同的变量的时候,...1.数组的初始化方式及其内存分配 对于Java数组的初始化,有以下两种方式,这也是面试经常考到的经典题目: 静态初始
  • JAVA对象在JVM中内存分配

    千次阅读 2017-04-22 13:50:07
    如果你还不了解JVM内存模型的建议您先看下JVM内存模型以一下代码为例,来分析下,java对象在内存中的空间分配。public class Student { private String name; private static Birthday birthday = new Birthday(); ...
  • 在【java虚拟机系列】java虚拟机系列之JVM总述我们已经详细讲解过java中的内存模型,了解了关于JVM内存管理的基本知识,接下来本博客将带领大家了解java中的垃圾回收与内存分配策略。 垃圾回收(Garbage ...
  • Java内存分配

    千次阅读 2016-03-25 22:30:37
    而对于Java程序员来说,JVM自动进行内存管理,程序员不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄露和内存溢出问题。 但是,正因为JVM帮我们管理了内存,一旦出现内存泄露或溢出问题,如果...
  • 再探Java内存分配

    千次阅读 多人点赞 2017-09-01 20:56:54
    我觉得:要回答这个问题不妨先搁置这个问题,先往这个问题的上游走走——Java内存分配。一提到内存分配,我想不少人的脑海里都会浮现一句话:引用放在栈里,对象放在堆里,栈指向堆。嗯哼,这句话听上去没有错;但是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 504,712
精华内容 201,884
关键字:

java中内存分配

java 订阅