一 java 堆内存和栈内存
java把内存分为两种:一种是堆内存,一种是栈内存
堆: 堆内存主要存储实例化对象,数组。由JVM动态分配内存空间。一个JVM只有一个堆内存,线程是可以共享数据的。
栈:主要存储局部变量和对象的引用变量。每个线程都会有一个独立的空间,所以线程之间的数据是不可以共享的。
在函数中定义的一些基本数据变量和对象的引用变量都会存储到栈内存中,当在一段代码中定义一段变量时,java就在栈内存中为这个变量分配内存空间,当超过这个变量的作用域后,java会自动释放掉该变量分配的内存空间,该内存的空间可以立即被另做它用。
堆内存用于存放新建new对象和数组。
在堆中分配内存,由java虚拟机自动垃圾回收来处理。
二、堆和栈的共同点和优缺点
1、栈(stack)与堆(heap)都是java用于在Raw存放数据的地方,与C++不同,java自动关了堆和栈,程序员不能设置堆和栈。
2、栈的优势是,存储数据快,仅次于cup中的寄存器,但缺点是,存在存在栈中的数据大小与生存期必须是确定的,缺乏灵活性,另外,栈内存数据可以共享(这可能跟上面说的有点冲突,上面刚说的数据不能共享,现在又说可以共享,很矛盾,但是,栈内存在同一个线程中的数据是可以共享的,其他线程是不共享的),堆内存的优势是可以动态的分配内存的大小,生存期也不必要事先告诉编译器,java垃圾回收机制会自动收走这些不在使用的数据,但缺点是,由于运行时是动态分配内存的,所以存取速度较慢。
3、java中的数据类型有两种
一种是基本数据类型(primitive types),共有8种,即 int,short,long,byte,float,double,boolean, char(童鞋们要注意了,这里string不是基本类型)这种类型的定义是通过诸如 int a=3; long b =255L; 的形式来定义的,称为自动变量,值得注意的是,自动变量存的是字面值,不是类的实列,即不是类的引用,这里并没有类的存在,如 int a = 3 ;这里的a是一个指向int类的引用,指向3的字面值,这些字面值的数据,大小可知,生存期可知(这些字面值固定定义在某个程序模块里面,程序块退化后,字段值就消失了),出于追求速度的就存在栈内存中。
栈有个特殊的重要性,就是存在栈中的数据可以共享。
假设我们同时定义
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后,b的字面值依然是3 ,不会因为a的字面值改变而改变。
另一种是包装类数据。如 Integer,String ,Double等将相应的基本数据类型包装起来的类。这些数据类型全部存储在堆内存中,java 用 new()语句来显示的告诉编译器,在运行是才根据需要动态创建,因此比较灵活,缺点是占用更多的时间
三、堆和栈的区别
1、各司其职
最主要的区别就是栈内存用于存储局部变量和方法的调用。而堆内存用来存储java中的对象,无论是成员变量,局部变量还是类变量,他们指向的对象都存储在堆内存中。
2、独有还是共享
栈内存归属于单个线程,每个线程都会有一个栈内存,其存储变量只能在所属线程中可见、共享,即栈内存可以理解为线程的私有内存。而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
3、异常错误
如果栈内存没有可用空间存储方法调用和局部变量,JVM会抛出Java.lang.StackOverFlowError
如果堆内存没有可用空间存储生成的对象,JVM会抛出Java.lang.OutOfMemoryError.
4、空间大小
栈的内存要原因小于堆内存,如果你使用递归的话,那你的栈内存会很快充满,如果递归没有及时跳出,很可能会发送StackOverFlowError问题。
你可以通过-Xss选项设置栈内存的大小。-Xss选项可以设置堆开始时的大小,-Xmx选项可以设置堆内存的最大值。
这就是java中的堆和栈,理解好这个问题,可以对你解决开发中的问题,分析堆和栈内存使用,甚至性能调优都有帮助。
四、java中的堆和栈
JVM是基于堆栈的虚拟机,JVM为每个新创建的线程都分配了一个堆栈,也就是说,对于一个java程序员来说。他的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保持线程的状态,JVM对堆栈只进行两钟操作:以帧为单位的压栈和出栈操作,我们知道,某个线程正在执行的方法称为次线程的当前方法,我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个java方法,JVM就会在java的堆栈中新压入一个帧。这个帧自然称为了当前帧,在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据。这个这在这里和编译原理中的活动记录的概念差不多的,从java的这种分配机制来看,堆栈又可以这样理解,堆栈(stack)是操作系统在建立某个进程时或者线程(在支持多线程操作系统中的线程)为这个线程建立的存储区域。该区域具有先进后出的特性。
每一个java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享,跟C/C++不同,java分配堆内存是自动初始化的,Java中所有对象的 存储空间都是在堆中分配的,也就是说在建立一个对象时,在两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆中分配的内存只有一个指向这个堆对象的指针(引用)而已。
Java把内存划分成两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配,当在一段代码定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java自动释放掉为该变量分配的内存空间,该内存空间可以立即另作它用。