内存
内存大家都知道(当然不是硬盘啊)。与c、c++相比呢,Java在内存管理的方面一个优越之处就是我们不用显式的去对对象进行内存的分配和内存的回收,可能有人会着迷于对内存使用分配的这种快感,但是随着程序变大,对于内存的维护工作也就越来越大。Java的JVM的自动内存管理机制,凸显出了强大的优越感。。。。
但反而是因为这样的一个现状,就弱化了我们在写Java程序时遇到内存溢出等问题时的定位能力和解决问题的能力。就在这个时候一本书应运而生--- 《深入理解java虚拟机》 这本书也算是我旁边落灰最严重的一本了,但写程序就是这样如果不沉到底,程序浮于表面那就只是单纯的应用,不能变的熟练。
只有我们真正的了解了JVM如何管理内存后,才能遇见OutOfMemory错误时,快速的根据异常日志信息定位和解决问题。
Java内存分配方式
咱们看看上面这张图,颜色这么鲜艳,这次一定能记住了!
-
静态 存储区
内存在程序编译的时候就已经分配好了,这块内存在程序的整个运行期间都存在。比如,static 、全局变量
-
在 栈 上创建
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分匹配运算内置于处理器的指令集中、效率很高、但是分配的内存容量非常有限。
-
在 堆 上分配
动态内存分配。在c和c++中运行程序时用 malloc 或 new申请任意大小的内存,我们需要自己决定自己在何时何地使用使用free和delete来释放内存。
Java虚拟机内存模型是Java程序运行的基础。虚拟机在执行Java程序的过程中会把他所管理的内存划分为若干的不同的数据区域,ok,这里加重 是分为不同的数据区域,这些区域都有自己的用途以及创建和销毁的时间。看一下下图,
太好了,又画了一个带颜色的图~那就说一说为什么带不同的颜色吧
- 紫色,由所有线程共享的数据区
- 线程隔离的数据区
程序计数器
寄存器里面有一个叫指令寄存器,用来储存现在正在被运行的指令。想象一下,在JVM中怎么办,程序寄存器就是这样的功能。
程序技术器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。“字节码”就是Java程序被编译之后的形态,JVM有字节码解释器,这个解释器要解释程序的哪段,就由这个程序技术器来决定的。
Java虚拟机栈
Java虚拟机栈也是线程私有的,生命周期与线程相同,虚拟机栈描述的是Java方法执行的内存模型。每个方法在执行的同时会创建栈帧,保存
- 局部变量表
- 操作数栈
- 动态链接
- 方法出口
每个方法从调用直到执行完成的过程,就对应整一个栈帧在虚拟机栈中入栈和出栈的过程。
平时咱们在讨论的时候总会提到“栈”和“堆”这两种内存区域,那么其中的栈,就是这里所指的栈,更细一点说,就是虚拟机栈中局部变量的部分。
在换一个方面讲解一下,虚拟机栈是用来被快速访问的存储区域,一般该区域位于通用RAM里,
这个RAM叫随机存储器,是与CPU直接交换数据的内存存储器,也叫主存,可以随时随地的写,而且速度快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。
在虚拟机栈中,使用栈指针来访问处理器。我们都学过栈这种数据结构,它是一种快速有效的 分配 存储的方法,存储速度仅次于寄存器,堆栈指针若向下移动,则分配新的内存,若向上移动则释放那些内存。由于Java编译器需要预先去生成相应的内存空间,所以,当我们尝试创建程序的时候,Java编译器必须知道被存储在站内的所有数据的确切大小和声明周期。一遍可以像上面描述的那样去分配内存空间。
栈相对于堆的优势就是比堆存取快,在栈中重要被用来存放一下基本类型的变量,例如int、short、long、byte、float、double、boolean、char,以及对象的引用(对象本身一般都存放在堆中)
StackOverFlow和OutOfMemoryError
Java虚拟机规范允许Java栈的大小是动态的或者是固定不变的。如果线程在计算过程中,请求的栈深度大于最大可用的栈深度,则在程序运行过程中会抛出StackOverFlow异常、如果Java栈可以动态扩展,而在扩展的过程中没有足够的内存空间支持栈的发展,在运行过程中会抛出OutOfMemoryError异常。
本地方法栈
本地方法栈与虚拟机栈所发挥的作用十分相似,区别就是虚拟机栈执行Java(字节码)的方法,本地栈是为虚拟机使用到Native方法。
Java堆
Java堆(Heap)是Java虚拟机所管理的内存中最大的一块。堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。次内存却与的唯一目的就是存放对象示例,刚才在栈的部分也说了,栈中存的是对象的索引,而对象的实例存放在堆中。
在Java虚拟机规范中描述:所有的对象实例以及数组都要被在堆上分配内存。
GC(Garbage Collection)垃圾回收
由于堆和栈结构上的不同,所以其内存回收的机制也是不一样的。
Java中对可以细分为:
- 新生代
- 老年代
再细分...
- Eden空间
- From Survivor空间
- To Survivor
在内存的角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓存区(Thread Local Allocation Buffer,TLAB)。Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样,在实现是,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的
-Xmx
-Xms
在堆中没有内存完成实例分配,并且堆也无法在扩展时会抛出OutOfMemoryError异常。
方法区
方法区和Java堆是一样的,是各个线程共享的内存区域,他用于储存已被虚拟机加载的类的信息、常量、静态变量、即时编译器编译后的代码等数据。