一个方法调用另一个方法,会创建很多栈帧吗?
会创建。如果一个栈中有动态链接调用别的方法,就会去创建新的栈帧,栈中是由顺序的,一个栈帧调用另一个栈帧,另一个栈帧就会排在调用者下面
递归的调用自己会创建很多栈帧吗?
答:递归的话也会创建多个栈帧,就是在栈中一直从上往下排下去
java堆(Java Heap)是java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,⼏乎所有的对象实例以及数组都在这⾥分配内存。
java堆是垃圾收集器管理的主要区域,因此也被成为“GC堆”。从垃圾回收的⻆度,由于现在收集器基本都采⽤分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老生代,再细致⼀点可分为:Eden 空间、From Survivor、To Survivor 空间等
根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。当前主流的虚拟机都是可扩展的(通过 -Xmx 和 -Xms 控制)。如果堆中没有内存可以完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
JDK1.8以前使用永久代(方法区),JDK1.8以后使用元空间
整个永久代有⼀个 JVM 本身设置固定大小上限,⽆法进⾏调整,而JVM加载的class的总数,方法的大小等都很难确定,因此对永久代大小的指定难以确定。太小的永久代容易导致永久代内存溢出,太大的永久代则容易导致虚拟机内存紧张,空间浪费。⽽元空间使⽤的是直接内存,受本机可⽤内存的限制,虽然元空间仍旧可能溢出,但是⽐原来出现的⼏率会更⼩。
元空间溢出时会得到如下错误: java.lang.OutOfMemoryError: MetaSpace
你可以使⽤ -XX MaxMetaspaceSize 标志设置最⼤元空间⼤⼩,默认值为 unlimited,这意味着它只受系统内存的限制。 -XX MetaspaceSize 调整标志定义元空间的初始⼤⼩如果未指定此标志,则 Metaspace 将根据运⾏时的应⽤程序需求动态地重新调整⼤⼩。
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致 OutOfMemoryError 异常出现。
直接内存可以看成是物理内存和Java虚拟机内存的中间内存,他可以直接使⽤ Native 函数库直接分配堆外内存,然后通过⼀个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引⽤进⾏操作。这样就能在⼀些场景中显著提⾼性能,因为避免了在 Java 堆和 Native 堆之间来回复制数据。
本机直接内存的分配不会受到 Java 堆的限制,但是,既然是内存就会受到本机总内存⼤⼩以及处理器寻址空间的限制。
| 对比 | JVM堆 | JVM栈 |
| — | — | — |
| 物理地址方面 | 堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,所以使用了各种垃圾回收算法 | 栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。 |
| 内存分配方面 | 堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈。 | 栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的。 |
| 存放的内容方面 | 堆存放的是对象的实例和数组。因此该区更关注的是数据的存储 | 栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。 |
| 程序的可见度 | 堆对于整个应用程序都是共享、可见的。 | 栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。 |
堆:主要用来存储对象和数组,物理地址分配不连续、内存大小不确定、线程共享
栈:用来存放操作数栈,物理地址分配连续、内存在编译期确定、线程私有
-
加载类元信息,