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  阅读(139)  评论(0编辑  收藏  举报