JVM学习(二)

栈 stack

栈 : 栈 内 存 。 主 管 程 序 的 运 行 》 生 命 周 期 和 线 程 同 步 。  线 程 结 束 , 栈 内 存 也 就 是 放 , 对 于 栈 来 说 , 不 存 在 垃 圾 回 收 问 题  一 目 线 程 结 束 , 栈 就 0 虻 0  栈 : 8 大 基 不 类 型 + 对 象 引 用 + 实 例 的 方 法  栈 运 行 原 理 : 栈 帧  栈 满 了 : StackOverflowError

栈帧

里面有 方法索引 输入输出(参数) 本地变量 class File:引用 (不懂这个是什么) 然后父帧 子帧

计算机生成了可选文字: 广法引: 00File 五幸弓刈

堆 Heap

一个jvm中只有一个堆内存 (栈是线程级的 一个线程就有一个栈空间)

Heap , 一 个 M 只 有 一 个 堆 内 存 , 堆 内 存 的 大 小 是 可 以 调 节 的 。  类 加 载 器 读 取 了 类 文 件 后 , 一 般 会 把 什 么 东 西 放 到 谁 中 ? 类 。 方 法 。 总 量 。 变 量 0 保 0 我 们 所 有 引 崩 类 型 的 頁  堆 内 存 中 还 要 细 分 为 三 个 区 过 :  新 生 区 ( 伊 回 园 区 ) Young/New  养 老 区 old  · 永 久 区 Perm

新生代 老年代 元空间

新生代(区) yound 中有伊甸园区和 幸存者0区 和幸存者1区

老年代(区) old

永久代(区) Perm (jdk 1.8 之后 改成了元空间 )

伊- (Fden 특F』a「이

GC  00M, java.lang.OutOfMemoryError: Java heap space

// 伊甸园的满了 会进行gc gc后不死的就会进入幸存者0区或幸存者1区

(轻gc到底是针对 伊甸还是伊甸和一个幸存者区的呢? 感觉还是会针对 一个幸存者区 看深入浅出java虚拟机里面说复制算法的时候也是带上了一个幸存者区)

每次往伊甸和一个幸存者区里面存 当这两个都满了 就进行一次轻gc 轻gc能活下的就进入另一个幸存者区 在

轻GC(是指针对 伊甸园和幸存者0区或幸存者1区的) 伊甸园的内存和幸存者0或幸存者1区满了 就进行一次轻GC 能够活下来的 就进入了幸存者0区或幸存者1区(总会有一个幸存者区是空着的 轻gc后的内存就放在那个幸存者区中)

当一经历一次轻gc不死 且幸存者区依然有位置存放这个对象 这个对象的年龄应付设为1 如果他能活到15岁(15次轻GC不死) 就可以进入老年区了

还有一种情况 假如那个幸存者区 不够空间放那些 活下来的 对象 多出来的 就直接进入到老年区了(看书里也有这种情况 自己第二次细看视频 和书 也才感觉到这一点)

重GC 当老年区中的内存满了的时候 会进行一次重GC 如果还能活下来的

当新生区和老年区都满了 就会oom 堆内存溢出

这 个 区 域 常 驻 内 存 的 。 用 来 存 湖 DK 鬥 身 携 带 的 Class 对 象 湖 nterface 元 数 啹 , 存 储 的 是 a 运 行 时 的 一 些 环 境 或  类 信 息 。 这 个 区 域 不 存 在 垃 圾 回 收 ! 关 闭 VM 拟 就 会 放 这 个 区 域 的 内 存 一  一 ^ 启 动 类 , 就 载 了 大 虽 的 第 三 ar 飢 Tom 儲 t 部 署 了 太 多 的 应 用 , 大 虽 动 态 生 成 的 反 射 类 ‰ 不 断 的 被 加 载 。 直  到 内 存 满 , 就 会 出 现 00M ;  · jdkl 毛 之 前  永 久 代 , 常 量 池 是 任 方 法 区 ;  永 久 代 , 但 是 嶧 的 退 化 了 ,  . jdkl , 7  · jdkl . 8 之 后 、 无 水 久 代 《 常 虽 池 任 元 空 间  去 永 久 代 , 常 量 池 在 堆 中

元空间中有方法区 方法区中有常量池

(Eden)  (Perm)  (Old)

oom(OutOfMemoryError)内存溢出与栈溢出(stock over Error)

oom 也有几种情况

自己知道两种 一种是 java.lang.OutOfMemoryError: Java heap space 堆内存溢出(应该是新生代或老年代随便一个溢出就报这个错误吧)

还有一个是java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出(但是jdk 1.8 已经没有永久代了 不知道还有没有这个错)

也是从这篇文章上面看的

Java OOM 常见情况

https://www.cnblogs.com/lsgxeva/p/10220889.html

堆内存调优

输出 jvm最大内存与初始化(最小)内存大小的代码

// jvm 最大内存 默认是本地总内存的四分之一

long maxMemory = Runtime.getRuntime().maxMemory();

System.out.println("jvm最大内存:"+maxMemory+"字节"+(double)(maxMemory/1024/1024)+"mb");

// jvm 初始化内存大小 默认是本地总内存的六十四分之一

long totalMemory = Runtime.getRuntime().totalMemory();

System.out.println("jvm初始化内存:"+totalMemory+"字节"+(double)(totalMemory/1024/1024)+"mb");

设置jvm最大内存与初始化(最小)内存大小的命令 要记住那几个参数 而且 一个都不能打错 还有是在那个地方写命令 也不要搞错了

// -Xmx1024m -Xms1024m -XX:+PrintGCDetails 一个字母都不能错...

//-Xmx1024m 设置jvm最大内存为1024m

//-Xms1024m 设置jvm初始化(最小)内存为1024m

//-XX:+PrintGCDetails 输出GC日志

//-XX:MaxTenuringThreshold=20 调整幸存区的对象 经历多少次GC后 进入老年代

jvm堆大小的设置 及一些参数是干嘛的

https://blog.csdn.net/zfgogo/article/details/81260172

调整幸存区的对象 经历多少次GC后 进入老年代(默认是经历15次GC不死就会进入老年代) 也是一种调优的方式

命令是-XX:MaxTenuringThreshold=20

在这个地方写(运行了这个方法后 那个锤子那里找到方法 然后在VM options:里面写命令)

image-20210105175348321

oom 也有几种情况

自己知道两种 一种是 java.lang.OutOfMemoryError: Java heap space 堆内存溢出(应该是新生代或老年代随便一个溢出就报这个错误吧)

还有一个是java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出(但是jdk 1.8 已经没有永久代了 不知道还有没有这个错)

也是从这篇文章上面看的

Java OOM 常见情况

https://www.cnblogs.com/lsgxeva/p/10220889.html

GC日志长这样

[GC (Allocation Failure) [PSYoungGen: 237801K->23536K(305664K)] 237801K->67879K(1005056K), 0.0158012 secs] [Times: user=0.02 sys=0.03, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 250285K->1080K(305664K)] 294629K->178431K(1005056K), 0.0293426 secs] [Times: user=0.03 sys=0.06, real=0.03 secs] 
[GC (Allocation Failure) [PSYoungGen: 188075K->1048K(305664K)] 542771K->444423K(1005056K), 0.0500933 secs] [Times: user=0.03 sys=0.03, real=0.05 secs] 
[Full GC (Ergonomics) [PSYoungGen: 97584K->0K(305664K)] [ParOldGen: 620719K->267305K(699392K)] 718304K->267305K(1005056K), [Metaspace: 4856K->4856K(1056768K)], 0.0487458 secs] [Times: user=0.13 sys=0.00, real=0.05 secs] 
[Full GC (Ergonomics) [PSYoungGen: 182044K->0K(305664K)] [ParOldGen: 621993K->178633K(699392K)] 804037K->178633K(1005056K), [Metaspace: 4856K->4856K(1056768K)], 0.0291505 secs] [Times: user=0.06 sys=0.00, real=0.03 secs] 
[GC (Allocation Failure) [PSYoungGen: 177344K->0K(305664K)] 710664K->533320K(1005056K), 0.0024000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(305664K)] 533320K->533320K(1005056K), 0.0023392 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(305664K)] [ParOldGen: 533320K->533320K(699392K)] 533320K->533320K(1005056K), [Metaspace: 4856K->4856K(1056768K)], 0.0995777 secs] [Times: user=0.14 sys=0.01, real=0.10 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(327168K)] 533320K->533320K(1026560K), 0.0024144 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(327168K)] [ParOldGen: 533320K->533239K(699392K)] 533320K->533239K(1026560K), [Metaspace: 4856K->4856K(1056768K)], 0.0983429 secs] [Times: user=0.16 sys=0.00, real=0.10 secs] 

oom后如果把堆内存调大了 还有问题那就很可能是代码有问题了

真实环境 肯定不可能是在那个地方去改堆内存大小 肯定是在那个地方有配置文件去更改

idea安装目录 idea.exe.vmoptions 在这个里面去改

https://jingyan.baidu.com/article/f25ef254b56549482c1b8290.html

Jprofiler 工具分析oom原因

本地要下载一个Jprofiler工具 然后Idea再安装一个对应的插件

在设置jvm参数那里 设置oom的时候会dump gc文件

命令 在oom错误的时候生成profile文件

-XX:+HeapDumpOnOutOfMemoryError

oom错误 后 就会生成一个文件 用本地工具打开 就能看到信息了

biggest object 看看大对象 有那些…

image-20210105175423437

GC(垃圾回收算法)

引用计数法

(基本没有人在用了)

一个对象一个计数器 记录有多少个地方或这个对象有多少个引用

但是对象太多了 计数器也会很多 且计数器也要运行 太消耗性能了

囗 ㄖ 囸

复制算法

新生代里面有伊甸园区和两个幸存者区(一个from 一个to ) to区只用来存放GC后还能活在的对象 GC后to区就变成了from区 原来的from区就变成了to区

​ 如果伊甸园中的内存又满了 对象就会往这个from区中存 GC后 依然将存活的对象放到to区 to区变成from 区 现在的from区变成to区

对象在伊甸园出生,对象会存储在伊甸园区和某个幸存者区(from区) 当伊甸园和from区都存储满了 就会进行一次轻GC

如果GC后 还能活下来的对象 就放到幸存者区(to区)

to区就变成了from区 原来的from区就变成了to区 就这样周而复始

两种情况下 幸存区中的对象会进入老年代

1 对象经历15次GC不死的(默认情况下) 进入老年代

2 假如伊甸园和from区满了 进行轻GC 但是GC后存活的对象很多 to区不足以存放 那么多出来的对象 就会直接进入老年借

优点 没有零碎内存

缺点 要浪费一个to区的内存

这种算法适合在新生代使用 (新生代中死亡的对象很多 所以要复制的对象就没有那么的多)

调整 幸存区的对象 经历多少次GC后 进入老年代(默认是经历15次GC不死就会进入老年代) 也是一种调优的方式

调整成经历20次GC不死就会进入老年代

命令是-XX:MaxTenuringThreshold=20

1 0 次 《 还 都 昙 卜 d 和 n 活 的 对 移 到 萍 区 中 :  旦 “ 区 0 〔 后 , 就 会 过 空 的 》  # 个 娟 象 而 了 15 《 0 都 还 胥  -XX: 0 : M “ T 起 nu 山 叫 T 卜 " 以 h 囗 记 。 99  i 到 《 汶 饣 0 瓠 可 以 过 讲 人 过 年 代 〕 下 力 〗  主 要 的 夏 制 算 法  章 区 to  幸 区 from { 門  元 空 同

image-20210105175537695

标记清除算法

把有引用的对象进行标记 进行GC的时候 就将没有标记的对象干掉

优点 没有浪费内存

缺点 有很多零散空间(内存碎片) 内存空间不连续

囗 囗

标记整理(压缩)算法

和上面的标记删除差不多 只是再多扫描一次 把活着的对象都移动到另一端去

优点 没有内存碎片了

缺点 又进行了一次扫描 浪费时间

辷 に ま と 対 : 対 彧 者 対 象 選 行 物  ロ  ヒ 能 : 防 止 内 第 片 デ 亠 . 用 次 屋 . 向 - 取 移 存 活 〕  多 了 - す  ロ ロ ロ  ロ

总 结  内 存 效 率 : 复 制 法 > 际 记 清 隊 法 冫 标 记 压 缩 法 ( 时 间 复 杂 度 〕  内 存 整 齐 度 . 0 制 去 = 标 记 珏 缩 的 法 > 标 记 洁 除 算 氵 去  内 存 利 用 率 : 标 记 压 缩 法 = 际 记 清 除 法 > 复 制 去

JMM (java 内存模型)

java内存模型(Java Memory Module)

是一套逻辑规则,主要是用来解决不同系统/平台间内存访问的差异,使java程序在不同的平台上能达到一致的内存访问效果。

java中有一个主内存 里面有一些共享变量 每个线程都有自己的工作内存

每个线程需要用到主内存中的变量 都是从主内存中copy出来的 假如有两个线程 都要用那个变量(a=0)

(假设并发是2 因为电脑只有一核 所以同一时间其实只有一个线程在跑)

第一个线程把a改成了1 但是主内存那边不知道 a 已经变成1了 第一个线程还没有来得及把a同步给主内存 然后就到了第二个线程执行了(得到了cpu的运行权)

第二个线程进来了

第二个线程 还是从主线程中copy 拿到的a还是0 然后进行a =a+1 然后a就等于1了

这时 又轮到第一个线程执行了 第一个线程返回a= 1 线程结束

第二个线程执行 返回了a = 1 本来 期待的是主内存中的变量a = 1 然后第二个线程执行完了后 a = 2 但是现在依然只得到了a = 1 这个结果

然而 这个结果不是我们想要的 因为线程中的工作内存没有及时同步到主内存中去 所以有问题

就制定了 jmm内存模型 希望解决类似的问题。

valtile可以解决内存不可见的问题

但是还有原子问题 ABA等问题(看以前juc的笔记)

jmm的八个指令(四组)

image-20210105175652816

image-20210105175709103

posted @ 2021-01-06 08:53  cnng  阅读(179)  评论(0)    收藏  举报