JavaWeb系列读书笔记(六)JVM内存管理

物理内存和虚拟内存

物理内存就是RAM和寄存器(用来存储计算单元执行指令的中间结果)。连接处理器和RAM或者寄存器和处理器的是地址总线。这个地址总线的宽度影响物理地址的索引范围,因为总线的宽度决定了一次可以从寄存器或者内存中获取多少个bit。
通常地址总线和寄存器和RAM有相同的位数,更容易传输数据。

一般来说如果要直接让程序直接访问存储器外,大部分是通过操作系统提供的接口来实现。但是在Java不用写和内存相关的代码。

我们运行程序都要申请内存地址。而通常操作系统管理内存是按照进程方式处理,每个进程都独享一个独立的内存空间,互不打扰,但这个互不打扰和独立性是通过操作系统来保证的。

而这个独立性更多也只是一个概念性的东西,实际上物理内存可以被多个运行进程共享,这个称为虚拟内存。

概念性大概就是计算机口头上是不承认自己的物理内存是共享的,而表面上看起来也是进程内存使用相互独立的,但是从底层的角度考虑其实物理内存是共享的。

而完成所谓的逻辑独立和实际不独立的方法就是通过虚拟内存技术,具体来说是虚拟地址。让物理内存被共享,提高内存利用,还能拓展内存的地址空间。一个进程不活动时候,会将这个进程对应的数据移动到一个磁盘文件中(通常为windows的页面文件或者是linux的交换分区),这样就腾出内存给活动程序用啦。当我们唤起这个很久没使用的程序,磁盘就开始响,而且会停顿,将磁盘中的数据重新放到物理内存中,这操作很低效率。

内存空间

内存空间会被划分成内核空间和用户空间,程序只能申请到用户空间的内存,但实际上程序也能访问到内核空间。

内核空间主要适用于操作系统运行的程序调度,虚拟内存使用,硬件资源连接等程序逻辑。就是给操作系统用的。所以用户程序不能直接访问硬件资源,要通过操作系统发起。每一次系统调用都会存在内存空间的切换,

Java堆

Java堆是用来存储Java对象的区域,堆的大小从JVM启动时就确定,可以通过-Xmx和-Xms来控制申请大小,但堆的大小申请后就不能改变

线程JVM的运行实体,每个线程创建时JVM都会为它创建一个堆栈,

类和类加载器

这两个本身就需要存储空间,他们也被存储在堆中,这种区域叫永久代?(PermGen区域)

需要注意JVM按需加载类,只会加载在应用程序指明的类,一个类一般而言只会加载一次,倘若你的类加载器是自己实现的可能会出现重复加载的情况,如果PermGen区不能堆已经失效的类做卸载,可能会导致PermGen区内存泄漏。任何系统类或者通过程序类加载器的加载的任何程序类都不能在运行时释放。

JNI 也会占用内存,使得本机代码(C语言)可以调用Java方法

JVM内存结构

JVM按照数据的存储结构来划分内存,将其分成几种不同格式的数据,分别存储在不同的区域,这些数据称为运行时数据。
运行时数据划分如下
1.PC寄存器数据
2.Java栈
3.堆
4.方法区
5.本地方法区
6.运行时常量池

PC寄存器

保存当前执行程序的内存地址,由于Java程序多线程进行,所以多线程交叉执行时,及时保存下来程序的内存地址就很重要了。

Java栈

每创建一个线程,JVM就会为这个线程创建一个对应的栈,这个栈又会含有多个栈帧。栈帧和方法相联系,每调用一个方法就创建了一个栈帧。存储一些内部变量,操作栈,返回值等信息。
Java栈顾名思义就是栈,它塞的元素是栈帧,在Java栈顶的地方才是当前执行的活动栈,一旦结束工作就会返回值然后顶部的栈帧被弹出执行下一个栈帧。Java栈不存在信息共享,所以里面的变量不用担心同步的问题

存储Java对象的地方,每一个存储在堆中的Java对象都是对象类的一个副本,堆是被所有Java线程所共享,所以应该注意同步问题。

方法区

用来存储类结构信息的地方,程序启动一段时间大小固定。是属于堆的一部分。

运行时常量池

放常量,属于方法区的一部分。

本地方法栈

为运行本地方法准备的空间,由于很多本地方法使用C语言数显,所以也叫C栈。JIT技术就是将一些Java方法重新编译成本地代码,然后哦这些编译后的本地代码就是用这个栈跟踪方法执行状态。

内存分配策略

  • 静态内存分配
  • 栈内存分配
  • 堆内存分配

静态内存分配指在程序编译时就能确定每个数据在运行时需求,然后就可以分配固定的内存空间。不允许在程序中出现可变数据结构,或者嵌套或递归,因为难以计算存储空间需求。静态内存分配效率最快。

栈内存分配也称动态分配,运行时才知道内存大小,但规定进入一个程序模块的时候就要知道数据大小并分配内存。和结构栈一样,栈式内存分配按照先进后出的形式分配

堆分配,是真正运行到相应代码才知道内存空间需求,这时候要用堆分配。

JVM内存回收策略

如何识别垃圾,只要某个对象不再被其他活动对象引用,那么这个对象就可以回收。这里活动对象指能被一个根对象集合到达的对象。

根对象集合如下:

  • 在方法中局部变量区对象的引用
  • Java操作栈的对象的引用
  • 常量池对象的引用
  • 本地方法中持有的对象的引用
  • 类的Class对象

垃圾收集算法有很多

但是大同小异,基本是基于分代的方法。

我们会将堆分成三个子堆,Young Old Perm三个区

1.Young区就是新对象申请的内存的位置。一般来说Young区会分成Eden区和Survivor区域,新生成的对象放在Eden区,当Eden区满时会触发minor GC 回收Eden区中不活动的对象,然后存活的对象会移动survivor,然后survivor满了会移动到old区。

2.一般来说,old区是来自young区的对象,但是也会出现新对象直接生成在old区的情况。当old区满了会触发Full GC机制。

3.Perm区主要是存放类的class对象。

4.Full GC会回收一切堆中不活动的对象

你可能感兴趣的:(JavaWeb系列读书笔记(六)JVM内存管理)