jvm笔记

运行时数据区包括:
堆、方法区、虚拟机栈、本地方法栈、程序计数器;
(其中堆、方法区是线程共享的;其他3个是线程私有的;)

程序计数器是该线程执行字节码的指示器(用于控制分支、循环、跳转、异常处理、线程恢复等);(此区域没有OOM)
虚拟机栈描述java方法执行的内存模型;(此区域有OOM和StackOverFlowError)
本地方法栈和虚拟机栈类似,只不过它为native方法服务;(注意:在sun hotspot中两者是合二为一的)(此区域有OOM和StackOverFlowError)

堆是内存最大的区域,所有对象都在堆上分配;(虽然栈上分配、标量替换会使之不那么绝对)
堆也是垃圾回收的主要区域,也叫GC堆;
根据分代收集算法,堆分为新生代、老年代;其中新生代又包括Eden空间、From Survivor空间、To Survivor空间;
(堆虽然是线程共享的,但也有TLAB,即线程私有的分配缓冲区;存放的仍然是对象;)(此区域有OOM)
堆内存只需要逻辑上连续即可,物理上不用连续,用-Xms和-Xmx控制堆大小;(此区域也有OOM)

方法区存放被jvm加载的类信息、常量、静态变量、编译后的代码等,也叫Nonheap即非堆;
(注意:hotspot将方法区称为永久代,其他虚拟机不存在永久代的概念;)
(这个区域的GC主要针对常量池的回收和类型的卸载;) (此区域也有OOM)

运行时常量池是方法区的一部分;(此区域也有OOM)

直接内存是指用native方法直接在堆外分配内存;(此区域也有OOM)

内存泄露和内存溢出:
内存泄露是指泄露对象无法达到GC Roots的引用链,导致无法回收对象;
内存溢出即OOM,即无法分配足够的内存;

-Xss10M -Xmx10M -Xms10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
(-Xms和-Xmx一样,避免堆内存自动扩展)
(-XX:+HeapDumpOnOutOfMemoryError用于将jvm出现异常时的信息dump出来;)
(-Xss是栈大小;-Xmn是新生代大小;)

注意:可通过‘减小内存’来解决‘内存溢出’问题;
(当每个线程的内存减小时,能创建的线程数就多了!!!)

-XX:PermSize10M -XX:MaxPermSize10M -XX:+PrintGCDetails
(-XX:PermSize和-XX:MaxPermSize用于限制方法区大小;同时间接限制常量池大小)

-XX:MaxDirectMemorySize用于指定直接内存;不指定默认与-Xmx一样大;
如unsafe.allocateMemory(1M)就是在直接内存分配的;

由于‘引用计数算法’的缺陷(当两个对象互相引用时,无法将它们回收),java没有使用该GC算法;
java采用的是‘根搜索算法’,即GC Roots Tracing,最大的区别是,从指定的‘起点’出发,即GC Roots。而不是任意起点,比如内部节点;

垃圾收集算法包括‘标记-清除算法’、‘复制算法’、‘标记-整理算法’、‘分代收集算法’;
(‘标记-清除算法’会产生大量碎片,效率太低;)
(‘复制算法’将内存分为2块,只使用其中一块;每次回收前,复制到另一块,从而避免碎片;)
(注意:‘复制算法’不必是1:1,太浪费,而是Eden:From:To=8:1:1,这样使用率为90%;若survivor不够,需要老年代担保)
(‘标记-整理算法’与‘标记-清除算法’类似,区别是,不直接回收,而是整理并移动到一端;避免了‘复制算法’的浪费和空间担保;)
(‘分代收集算法’将堆分为新生代和老年代;其中新生代可以用‘复制算法’,老年代用另外两种算法;)

垃圾收集器包括‘Serial收集器’、‘ParNew收集器’、‘Parallel Scavenge收集器’、‘Serial Old收集器’、‘Parallel Old收集器’、‘CMS收集器’、‘G1收集器’;
(其中新生代的有‘Serial收集器’、‘ParNew收集器’、‘Parallel Scavenge收集器’;)
(老年代的有‘CMS收集器’、‘Serial Old收集器’、‘Parallel Old收集器’;)
(‘Serial收集器’和‘Serial Old收集器’是单线程的,会导致‘stop the world’;参数为-XX:+UseSerialGC)
(‘ParNew收集器’是‘Serial收集器’的多线程版本;注意:只有它能与‘CMS收集器’搭配使用,除了单线程的收集器;)
(‘Parallel Scavenge收集器’和‘ParNew收集器’相似;但它的关注点不同,它关注吞吐量,而不是减少gc停顿时间;也叫‘吞吐量优先’收集器)
(吞吐量适用于后台任务,不需要太多交互的任务,使用-XX:GCTimeRatio;gc停顿时间适用于与用户交互的任务,使用-XX:MaxGCPauseMillis;)
(‘Parallel Scavenge收集器’还支持使用-XX:+UseAdaptiveSizePolicy来自动设置参数;)
(‘Parallel Old收集器’是‘Parallel Scavenge收集器’的老年代版本;)
(‘CMS收集器’以最短gc停顿时间为目的;参数包括-XX:+UseCMSCompactAtFullCollection、-XX:+CMSFullGCsBeforeCompaction、
-XX:+CMSInitiatingOccupancyFraction、-XX:+UseCMSInitiatingOccupancyOnly、-XX:+CMSScavengeBeforeRemark等;)
(‘G1收集器’基于‘标记-整理算法’,而‘CMS收集器’基于‘标记-清除算法’;)
(‘G1收集器’将java堆,包括新生代和老年代,划分为多个大小固定的Region,跟踪垃圾堆积程度,优先回收垃圾最多的区域,这是G1名字的由来;)

注意:-XX:+UseConcMarkSweepGC在指定CMS的同时,会默认使用‘ParNew收集器’,也可以使用-XX:+UseParNewGC强制使用它;

-XX:+UseParallelGC、-XX:+UseParallelOldGC分别使用‘Parallel Scavenge’+‘Serial Old’和‘Parallel Scavenge’+‘Parallel Old’;

-XX:+SurvivorRatio设置新生代的Eden:Survivor,默认8:1

-XX:+HandlePromotionFailure设置是否允许分配担保失败;(虽然担保失败绕的圈子是最大的,但为了避免Full GC过于频繁,通常还是会开启的;)

-XX:+PretenureSizeThreshold设置直接晋升到老年代的对象大小;

-XX:+MaxTenuringThreshold设置晋升到老年代的对象年龄;

-XX:+ParallelGCThreads设置并行GC时的线程数;

jvm内存分配的几个原则:

  1. 对象优先在Eden分配 2. 大对象直接进入老年代 3. 长期存活的对象进入老年代

注意:新生代GC通常只是Minor GC,老年代GC一般是Full GC(或Major GC)和Minor GC同时进行;
(Full GC/Major GC比Minor GC慢10倍以上;)
(对象每熬过一次Minor GC年龄加1;默认15岁进入老年代;可以用-XX:+MaxTenuringThreshold改变)

http://www.mamicode.com/info-detail-644957.html
(JVM最多可创建多少线程的估算公式)

-XX:+PrintCompilation打印JIT编译信息;

-XX:+PrintInlining打印方法内联信息;

-XX:+PrintLIR和-XX:+PrintOptoAssembly分别用于Client VM和Server VM;

-XX:+PrintCFGToFile和-XX:+PrintIdealGraphFile分别用于Client Compiler和Server Compiler,输出CFG和IdealGraph文件;

-XX:+UnlockDiagnosticVMOptions用于打开虚拟机诊断模式;

-verbose:class -XX:+TraceClassLoading查看类加载过程;

静态分派属于多分派(重载);动态分派属于单分派(重写)

目前理解的java解释器和编译器:

  1. javac是编译器,输出结果为字节码(早期)
  2. java -Xcomp -version、java -Xint -version打印出的interpreted mode、compiled mode是虚拟机层面的及时编译器如c1、c2或解释器。
    若是解释模式,是对字节码进行解释执行;若是编译模式,是对字节码进行继续编译得到本地机器码 (晚期)
    (注意:java -version会打印 mixed mode,即晚期中同时有解释模式和编译模式,不管c1还是c2的JIT编译器;)
    (此外,hotspot还有分层编译,即第0层是解释执行,第1层是c1编译,第2层是c2编译;使用-XX:+TieredCompilation参数设置;)

早期:javac; 晚期:解释器or即时编译器

javac字节码编译器和虚拟机内的JIT编译器合并起来等于传统的编译器;

AOT直接将java文件编译为本地机器码

早期如javac的优化只能让java享受到;晚期如JIT编译器的优化能让所有语言的class享受到,如Groovy

通过逃逸分析,确定对象作用域,当确定它无法被外部方法或线程访问时,就能对该变量进行高效优化,比如栈上分配、同步消除、标量替换等;
(用-XX:+DoEscapeAnalyisis开启逃逸分析;用-XX:+PrintEscapeAnalyisis查看分析结果;)
(用-XX:+EliminateAllocations开启标量替换;用-XX:+EliminateLocks开启同步消除;用-XX:+PrintEliminateAllocations查看标量替换情况;)

https://www.cnblogs.com/KingIceMou/p/7239668.html (java字节码和native方法的关系,见图)

JMM是一个进程一套,JMM是虚拟机内存的一部分(是共享的那部分),每条线程有自己的工作内存(working memory,存了主内存的拷贝,不能直接读写主内存,线程之间传递变量需要主内存)

如果勉强将主内存和工作内存与运行时数据区对应,那么主内存对应的是堆,工作内存对应的是栈;

JMM定义了8种操作:lock、unlock、read、load、use、assign、store、write;
(用于描述主内存和工作内存之间的变量传递和赋值操作;)
(lock、unlock对应更高层面的字节码指令monitorenter、monitorexit,以及java代码的同步关键字synchronized;)

注意:以上8个操作是原子的,但对于long、double有非原子性协定,即允许64位的类型分两次32位的执行;

volatile变量的语义:

  1. 可见性:volatile变量不存在一致性问题,即会立即对所有线程可见。但若加上对变量的操作,就不能保证同步了,仍需要额外同步;
  2. 禁止指令重排序优化;

除了volatile关键字,synchronized和final也能实现可见性;
volatile和synchronized都能实现有序性;(synchronized是全能的,同时实现了原子性、可见性、有序性;)

jvm除了靠volatile和synchronized实现有序性,也依赖“先行发生原则”实现有序性;(包含了多条规则)

jvm锁优化技术包括:

  1. 自旋锁和自适应自旋 2. 锁消除 3. 锁粗化 4. 轻量级锁 5. 偏向锁

-XX:+UseSpinning开启自旋锁;-XX:+PreBlockSpin设置自旋次数,默认为10;

hotspot虚拟机‘对象头’分为两部分:

  1. 对象自身运行时数据,如哈希值、gc年龄;是32位或64位,也叫Mark Word;
  2. 指向方法区对象的指针;
    (‘对象头’是实现轻量级锁和偏向锁的关键;)

轻量级锁:00和01,通过CAS,无需阻塞
重量级锁:10和11,通过互斥量,需要阻塞

偏向锁是指偏向第一个获得该对象锁的线程。若有新的线程尝试获取,偏向宣告结束。适用于大部分情况只有一个线程的时候。
(-XX:+UseBiasedLocking开启偏向锁;)

锁的升级:无锁状态–偏向锁–轻量级锁–重量级锁,其中轻量级锁就是自旋实现!!!

偏向锁和无锁都是01,但有一个字段叫“是否偏向锁”,一个是0,一个是1;
http://cmsblogs.com/?p=2071 !!!!!!)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值