27-内存知识
基础概念
内存又叫主存,是CPU与其他设备沟通的桥梁,主要用来临时存放数据,配合CPU工作,协调CPU的处理速度
- 硬盘数据,外设数据,玩了传输数据,要进CPU前,都要先进入内存
- 临时存放,在断电后,内存内容就会丢失
内存的组成:内存地址+存储单元
存储单元-数据结构
堆栈
两种不同的数据结构
堆(heap)
一种经过排序的树型数据结构
存放程序的对象
链表
数组、列表:数据是有顺序的,从左到右,从0开始。如果要在列表中,插入一个数据,那么在插入位置之后的数据,都需要移动,删除列表中间某个数据,在位置之后的数据,也都要移动。
链表也是一种数组,它的每个数据存储的都是数据值+下一个元素的地址。
如果要在链表中,插入一个数据。插入位置「前一个元素」中的「下一个元素的地址」需要指向插入的数据的地址,「待插入的元素」记录「下一个元素的地址」。
查找一个数据时,需要从头开始读取数据,一个一个的比对,直到找到需要的数据才停止。这个过程会有大量的IO,所以读取数据速度并不快。
- 二叉树
建立在链表的基础上的一种数据结构
二叉树左边存储的是小于自身数据,右边是存储大于自身数据
插入数据:因为是链表,插入速度也比较快
读取数据:因为数据已经做了二分,查找链路变短,IO就减少了,读取速度也变快
二叉树的不足:随着存储的数据量增大,二叉树会越来越大,那要查找某个数据的IO次数,也会非常多
- B树「平衡二叉树」
不是一个简单的平衡二叉树,是一个立体的平衡二叉树
B树和平衡二叉树稍有不同的是B树属于多叉树又名平衡多路查找树(查找路径不只两个),数据库索引技术里大量使用者B树和B+树的数据结构
栈
LIFO「Last In First Out」后进先出
装入叫压入
取出叫弹出
队列
FIFO「First In First Out」先进先出
- 顺序队列
- 循环队列
内存使用
一个程序运行起来,需要分配一块内存空间,无异常时,就在分配的空间中弹性伸缩存储
这个存储空间至少包含一块栈区,一块堆区,还会包括其他
- 栈区:存放程序中的变量
- 堆区:存放程序中的对象
JVM java虚拟机
程序计数器、java虚拟机栈、本地方法栈、方法区、堆内存
- 程序计数器:记录持续执行字节码的行号指示器
- java虚拟机栈:java方法执行时的内存模型
- StackOverflowError:线程请求的栈深度大于虚拟机运行的最大深度
- OutOfMemoryError:栈在动态扩展时,无法申请到足够的内存空间
- 内存泄漏:程序运行时,申请的内存空间使用完了,不及时释放,导致可申请的内存空间越来越少。可以使用的内存空间越来越少。
- 方法区:共享内存区域,存储已被虚拟机加载的数据
- 栈内存:存储局部变量,变量有一定的作用域,离开作用域,空间就会被释放,所以更新速度快,生命周期短
- 堆内存:存储数组和对象,new出来的都存堆里,如果数据消失,实体不会马上释放
OOM会导致整个内存条空间全被使用吗? 不会!只会消耗该程序申请的空间,不会消耗内存条全部空间
堆内存
- 新生代=Eden(存放JVM刚分配的对象) + Survivor1 + Survivor2 (两个空间一样大,Eden中未被GC的对象,会在这两个区间来回拷贝,默认拷贝超过15次,就移入年老代)
- Tenured 年老代
- Perm 永久代(元空间)
内存空间的释放
GC资源回收
需要有GC,但是频率要合理,不能过高
怎么判断是可回收
- 是否可达
- 是否存活
判断哪些可被回收
- 不要的、不能被回收:新生代、年老代
- 拷贝算法:把新生代变为年老代
什么时候回收
- 分配的空间不足,才会执行回收
- 定时回收
怎么回收
垃圾回收算法:新生代-复制算法「清理Eden,将存活的复制到Survivor」,年老代-标记整理算法「先标记,再整理」
资源回收
只有在讲「堆」的时候,才会说资源回收。本地方法栈、程序计数器、虚拟机栈这些是不需要进行垃圾回收的
Java的内存回收机制
内存空间中垃圾回收的工作由垃圾回收器「Garbage Collecot,GC」完成。它的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。
参数含义
-Xms
:初始堆大小-Xmx
:最大堆空间-Xmn
:设置新生代大小-XX:SurivivorRatio
:新生代eden空间,from空间,to空间的比例关系-XX:PermSize
:方法区初始大小-XX:MaxPermSize
:方法区最大值-XX:MetaspaceSize
:元空间GC阈值-XX:MaxMetaspaceSize
:最大元空间大小-Xss
:栈大小-XX:MaxDirectMemorySize
:直接内存大小,默认为最大堆空间
查看内存
1 | root@zx:~# free -h |
Mem:物理内存
- total合计
- used已被用
- free未使用
- shared共享
- buff/cache缓冲区、缓存
- buff:对原始磁盘块(操作系统与磁盘交流的最小单位)的临时存储
- cache:从磁盘读取文件的页缓存
- available新进程可分配=free+可回收的
Swap:交换分区
一种虚拟内存,由磁盘虚拟化而来,存在于内存和磁盘之间,因为磁盘和内存之间速度存在差异
1 | top - 14:51:35 up 169 days, 4:55, 1 user, load average: 0.01, 0.02, 0.00 |
VIRT 虚拟内存使用量 = Swap + RES
RES 物理内存使用量+未换出的虚拟内存大小 = CODE + DATA
SHR 共享内存使用量
Swap 虚拟内存中被换出的大小
CODE 代码占用的物理内存大小
DATA 代码之外的部分占用的物理内存大小
%MEM 使用的物理内存占总内存的比率
内存分析
jmap
命令:jmap [options] pid
-dump
:生成java堆栈的快照信息-heap
:显示java堆详细信息,使用那种回收机制,参数配置,分代情况-histo
:显示堆中对象统计信息,包括类,实例数量
1 | jmap -F -dump:format=b,file=jvmpertest110901.bin 3636 |
这个命令,执行时间很长,生成的文件也很大
arthas
阿里巴巴开源的java诊断工具,实现了jvm自带的几乎所有诊断功能
安装
1 | curl -O https://arthas.aliyun.com/arthas-boot.jar |
工具上手
1 | java -jar arthas-boot.jar |
内存泄漏抓包
- dashboard
- heapdump
jvm分析
输出gc日志
jvm的启动参数中加入
1 | -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimestamps -XX:+PrintGCApplicationsStopedTime |
启动后输出:GC概要信息,详细信息,gc时间,gc造成的应用暂停时间
jcosole
jdk自带的内存分析工具,有图形界面,可以查看jvm内存信息,线程信息,类加载信息,MBean信息
1 | jconsole.sh pid |
jstat
jdk自带的分析gc工具,参数很多
1 | jstat -gcutil pid 10000 间隔10000毫秒显示一次gc信息 |