一、了解堆和栈
JVM是基于堆栈的虚拟机,堆栈是一种数据结构,是用来存储数据的。对于一个java程序来说,它的运行用就是通过对堆栈的操作完成的。
栈内存用于存储局部变量,以及对象的引用,它是一个连续的内在空间,由系统自动分配,也就是说栈空间的分配和回收是由系统来做的,我们不需要手动控制。它的性能较高,栈内存具有先进后出、后进先出的特点,虚拟机会为每条线程创建一个虚拟机栈,当执行方法时,虚拟机会创建出该方法的一个栈帧,该方法中所有的局部变量都会存储到这个栈帧中,方法执行完毕后,栈帧弹栈。栈内存使用完毕后立即释放,生命值相对较短。
堆内存用于存储引用类型的数据,主要是对象和数组及字符串。全局只有一个堆内存,所有的线程共用一个堆内存。在堆中产生一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。引用变量就相当于为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量访问堆中的数组或对象了。使用后,等待JVM收取垃圾,生命值相对较长。
二、栈内存的优缺点
优点:对于栈内存,当一个函数调用时,系统就会为该函数的调用分配栈空间,当函数返回后,系统就会自动回收这块空间,同理,下次其它函数调用和返回,系统还是会自动分配和回收空间。线程安全,因为栈内存是线程独立的。
栈空间的大小是固定的,它有一个水位线,标识栈空间的分配状态,水位线里面的表示已经分配,然后这个水位线会根据函数调用和返回的情况自动调整。因此,栈空间的分配和回收非常简单,只需要调整水位线位置就可以了,没有任何多余操作。
缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性,快捷,但是自由度小。
三、堆内存的优缺点
优点:堆内存是区别于栈区、全局数据区和代码区的另一个内存区域。堆允许程序在运行时动态地申请某个大小的内存空间,自由度大,大小不固定,可以动态扩容。
缺点:
(1)C语言中常用的malloc()函数和new()函数分配的内存就是堆内存,堆内存容易产生内存泄露,比如malloc()出来的如果没有free(),new()出来的如果没有delete(),都会产生内存泄露。
(2)堆内存容易产生内存碎片,在分配和回收时需要对很多内存碎片进行整理,效率较低,因此要有很多自定义的内存分配器,但它依旧没有栈空间分配回收速度快。
(3)线程不安全,它不像栈内存是线程独立的,堆内存可以被一个进程内所有的线程访问,多线程操作就容易产生问题。
四、堆内存和栈内存的示例
上图程序的运行结果中,num1和 num2没有交换成功,而数组中索引为1和2位置却交换成功了,这可以从堆内存和栈内存的分析中推断出原因。
num1 和 num2是基本数据类且是局部变量,只会存储在栈帧中。由于Java中只有值传递,所以exchange 栈帧和栈帧中的num1和 num2是完全不同的两个变量,exchange 栈帧中发生的交换是成功的是对main 栈帧中的两个变量不会造成影响。
而arr是引用类型,数据实际上存储在堆内存中,main 栈帧中存储的是它的地址,作为0x1000。而Java 中只有值传递,因此main栈帧中的arr和exchange 栈帧中的arr值都是0×1000,但它们也是不同的两个变量。只不过引用类型数据操作比较特殊,是地址找到堆内存中对应的数据进行操作,而main栈帧与exchange 栈帧公用同一个堆内存,因此在exchange 中对堆内存进行的更改,在main栈帧中也可以感知到。