深入理解Java虚拟机

SpyGlass Lint脚本实战:从环境配置到报告生成的完整流程解析 本文详细解析了使用SpyGlass Lint脚本进行数字芯片设计代码质量检查的完整自动化流程。从环境配置、目录结构设计,到核心TCL脚本的编写与模块化配置,再到脚本执行、调试技巧及报告解读,提供了构建健壮、可维护自动化检查框架的实战指南。文章重点阐述了如何通过脚本实现从环境配置到报告生成的一键式操作,并探讨了与企业级CI/CD流程集成的进阶方案。 阅读详情

1 走近Java 7
1.1 概述 7
1.2 Java技术体系 7
1.3 Java发展史 7
2 Java内存区域与内存溢出异常 8
2.1 概述 8
2.2 运行时数据区域 8
2.2.1 程序计数器 8
2.2.2 Java虚拟机栈 8
2.2.3 本地方法栈 9
2.2.4 Java堆 9
2.2.5 方法区 9
2.2.6 运行时常量池 9
2.2.7 直接内存 9
2.3 对象访问 9
2.4 实战:OutOfMemoryError异常 10
2.4.1 Java堆溢出 10
2.4.2 虚拟机栈和本地方法栈溢出 11
2.4.3 运行时常量池溢出 11
2.4.4 方法区溢出 11
2.4.5 本机直接内存溢出 11
3 垃圾收集器与内存分配策略 12
3.1 概述 12
3.2 对象已死? 12
3.2.1 引用计数算法 12
3.2.2 根搜索算法 12
3.2.3 再谈引用 12
3.2.4 生存还是死亡? 12
3.2.5 回收方法区 13
3.3 垃圾收集算法 13
3.3.1 标记-清除算法 13
3.3.2 复制算法 14
3.3.3 标记-整理算法 14
3.3.4 分代收集算法 15
3.4 HotSpot的算法实现 15
3.4.1 枚举根节点 15
3.4.2 安全点 15
3.4.3 安全区域 15
3.5 垃圾收集器 15
3.5.1 Serial收集器 16
3.5.2 ParNew收集器 17
3.5.3 Parallel Scavenge收集器 17
3.5.4 Serial Old收集器 17
3.5.5 Parallel Old收集器 18
3.5.6 CMS收集器 18
3.5.7 G1收集器 19
3.5.8 理解GC日志 19
3.5.9 垃圾收集器参数总结 20
3.6 内存分配与回收策略 21
3.6.1 对象优先在Eden分配 21
3.6.2 大对象直接进入老年代 21
3.6.3 长期存活的对象将进入老年代 21
3.6.4 动态对象年龄判定 21
3.6.5 空间分配担保 21
4 虚拟机性能监控与故障处理工具 22
4.1 概述 22
4.2 JDK的命令行工具 22
4.2.1 jps: 虚拟机进程状况工具 22
4.2.2 jstat: 虚拟机统计信息监视工具 23
4.2.3 jinfo: Java配置信息工具 23
4.2.4 jmap: Java内存映像工具 24
4.2.5 jhat: 虚拟机堆转储快照分析工具 24
4.2.6 jstack: Java堆栈跟踪工具 24
4.2.7 HSDIS: JIT生成代码反汇编 25
4.3 JDK的可视化工具 25
4.3.1 JConsole: Java监视与管理控制台 25
4.3.2 VisualVM: 多合一故障处理工具 25
5 调优案例分析与实战 26
5.1 概述 26
5.2 案例分析 26
5.2.1 高性能硬件上的程序部署策略 26
5.2.2 集群间同步导致的内存溢出 26
5.2.3 堆外内存导致的溢出错误 26
5.2.4 外部命令导致系统缓慢 26
5.2.5 服务器JVM进程崩溃 26
5.2.6 不恰当数据结构导致内存占用过大 26
5.2.7 由Windows虚拟内存导致的长时间停顿 26
5.3 实战:Eclipse运行速度调优 26
6 类文件结构 27
6.1 Class类文件的结构 27
6.1.1 魔数与Class文件的版本 28
6.1.2 常量池 29
6.1.3 访问标志 31
6.1.4 类索引、父类索引与接口索引集合 31
6.1.5 字段表集合 31
6.1.6 方法表集合 32
6.1.7 属性表集合 33
6.2 Class文件结构的发展 37
7 虚拟机类加载机制 38
7.1 概述 38
7.2 类加载的时机 38
7.3 类加载的过程 38
7.3.1 加载 38
7.3.2 验证 39
7.3.3 准备 39
7.3.4 解析 40
7.3.5 初始化 41
7.4 类加载器 41
7.4.1 类和类加载器 41
7.4.2 双亲委派模型 41
7.4.3 破坏双亲委派模型 42
8 虚拟机字节码执行引擎 42
8.1 概述 42
8.2 运行时栈帧结构 42
8.2.1 局部变量表 42
8.2.2 操作数栈 43
8.2.3 动态连接 43
8.2.4 方法返回地址 44
8.2.5 附加信息 44
8.3 方法调用 44
8.3.1 解析 44
8.3.2 分派 44
8.4 基于栈的字节码解释执行引擎 45
8.4.1 解释执行 45
8.4.2 基于栈的指令集与基于寄存器的指令集 45
8.4.3 基于栈的解释器执行过程 45
9 类加载及执行子系统的案例与实战 45
9.1 概述 45
9.2 案例分析 46
9.2.1 Tomcat: 正统的类加载器架构 46
9.2.2 OSGi: 灵活的类加载器架构 47
9.2.3 字节码生成技术与动态代理的实现 47
9.2.4 Retrotranslator: 跨越JDK版本 47
9.3 实战:自己动手实现远程执行功能 47
9.3.1 目标 47
9.3.2 思路 47
9.3.3 实现 47
10 早期(编译器)优化 47
10.1 概述 47
10.2 Javac编译器 48
10.2.1 Javac的源码与调试 48
10.2.2 解析与填充符号表 48
10.2.3 注解处理器 49
10.2.4 语义分析与字节码生成 49
10.3 Java语法糖的味道 49
10.3.1 泛型与类型擦除 49
10.3.2 自动装箱、拆箱与遍历循环 49
10.3.3 条件编译 50
10.4 实战:插入式注解处理器 50
10.4.1 实战目标 50
10.4.2 代码实现 50
11 晚期(运行期)优化 50
11.1 概述 50
11.2 HotSpot虚拟机内的即时编译器 50
11.2.1 解释器与编译器 50
11.2.2 编译对象与触发条件 51
11.2.3 编译过程 52
11.2.4 查看与分析即时编译结果 53
11.3 编译优化技术 53
11.3.1 优化技术概览 53
11.3.2 公共子表达式消除 53
11.3.3 数组边界检查消除 54
11.3.4 方法内联 54
11.3.5 逃逸分析 54
11.4 Java与C/C++的编译器对比 54
12 Java内存模型与线程 54
12.1 概述 54
12.2 硬件的效率与一致性 54
12.3 Java内存模型 55
12.3.1 主内存与工作内存 55
12.3.2 内存间交互操作 55
12.3.3 对于volatile型变量的特殊规则 55
12.3.4 对于long和double型变量的特殊规则 56
12.3.5 原则性、可见性和有序性 56
12.3.6 先行发生原则 56
12.4 Java与线程 56
12.4.1 线程的实现 56
12.4.2 Java线程调度 59
12.4.3 状态转换 59
13 线程安全与锁优化 59
13.1 概述 59
13.2 线程安全 60
13.2.1 Java语言中的线程安全 60
13.2.2 线程安全的实现方法 60
13.3 锁优化 61
13.3.1 自旋锁与自适应自旋 61
13.3.2 锁消除 61
13.3.3 锁粗化 61
13.3.4 轻量级锁 61
13.3.5 偏向锁 62
14 附录 62
14.1 HotSpot虚拟机主要参数表 62
14.1.1 内存管理参数 62
14.2 对象查询语言(OQL)简介 62
15 其它 62
15.1 关于内存分配 62
15.2 其它 62

1 走近Java
1.1 概述
略过
1.2 Java技术体系
Java技术体系可以分为四个平台:
 Java Card
 Jave ME
 Java SE
 Java EE
1.3 Java发展史

2 Java内存区域与内存溢出异常
2.1 概述
2.2 运行时数据区域

2.2.1 程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
2.2.2 Java虚拟机栈
与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

两种异常状况:
 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;
 如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。
2.2.3 本地方法栈
2.2.4 Java堆
各个线程共享
理论上,所有的对象实例都在堆上分配内存,但是随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术的出现,这个已经不绝对了。
Java堆可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。
2.2.5 方法区
 各个线程共享
 用于存储已被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据。
 别名:Non-Heap(非堆)
 对于HotSpot虚拟机,有人称方法区为“永久代”。
 当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
2.2.6 运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。
2.2.7 直接内存
2.3 对象访问
Object obj=new Object();
 “Object obj”这部分的语义将会反映到Java栈的本地变量表中。
 “new Object()”这部分的语义将会反映到Java堆中。
 Java对中必须包含能查找到此对象类型数据的地址信息,这些类型数据存储在方法区中。

reference类型引用,主流访问方式有两种:使用句柄和直接指针。
 使用句柄:java堆中会划出一块内存来作为句柄池。

 直接指针方式:

2.4 实战:OutOfMemoryError异常
2.4.1 Java堆溢出
示例: 代码清单2-1 Java堆内存溢出异常测试

使用Eclipse Memory Analyzer对dump信息进行分析
Shallow Heap:对象自身占用的内存大小,不包括它引用的对象
Retained Size:Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。
相关资料参考:http://rdc.taobao.com/team/jm/archives/900
2.4.2 虚拟机栈和本地方法栈溢出
 HotSpot虚拟机不区分虚拟机栈和本地方法栈
 -Xss: 设定每个线程的栈大小
2.4.3 运行时常量池溢出
-XX:PermSize: 设置永久代最小尺寸,初始分配,示例:-XX:PermSize=64MB
-XX:MaxPermSize: 设置永久代运行分配的最大尺寸,按需分配,示例:-XX:MaxPermSize=256MB
过小会导致:java.lang.OutOfMemoryError: PermGen space

MaxPermSize缺省值和-server -client选项相关。
-server选项下默认MaxPermSize为64m
-client选项下默认MaxPermSize为32m

示例:代码清单2-4 运行时常量池导致的内存溢出异常
异常分析:抛出异常信息:
Exception in thread “main” java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at org.fenixsoft.test0243.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:19)
PermGen space,持久代空间不足,说明是方法区的溢出。
虽然Dump分析的是堆信息,但是也许能简介看出些问题。
2.4.4 方法区溢出
示例:代码清单2-5 借助CGLib使得方法区出现内存溢出异常。
2.4.5 本机直接内存溢出
DirectMemory容量可通过 –XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆的最大值(-Xmx指定)一样。
示例:代码清单2-6 使用unsafe分配本机内存
3 垃圾收集器与内存分配策略
3.1 概述
程序计数器、虚拟机栈、本地方法栈,这几个区域不需要过多考虑回收问题。
3.2 对象已死?
3.2.1 引用计数算法
Java语言没有选用引用计数算法来管理内存,最主要的原因是它很难解决对象之间的相互循环引用的问题。
示例:代码清单3-1 引用计数算法的缺陷
3.2.2 根搜索算法
在Java语言里,可作为GC Roots的对象包括下面几种:
 虚拟机栈(栈帧中的本地变量表)中的引用的对象。
 方法区中的类静态属性引用的对象。
 方法区中的常量引用的对象。
 本地方法栈中JNI(即一般说的Native方法)的引用的对象。

3.2.3 再谈引用
分为四种:强引用、软引用、弱引用、虚引用
 强引用:最普遍
 软引用:通过SoftReference类来实现软引用。内存溢出之前,会把这些对象列进回收范围进行第二次回收。
 弱引用:通过WeakReference类来实现弱引用。只能生存到下一次垃圾收集发生之前。
 虚引用:通过PhantomReference类来实现虚引用。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。
3.2.4 生存还是死亡?
主要介绍了回收的复杂过程,finalize()方法的作用。略过
3.2.5 回收方法区
 方法区(HotSpot虚拟机中的永久代)的垃圾收集“性价比”比较低。
 回收废弃常量:
 回收无用的类:
 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
 加载该类的ClassLoader已经被回收。
 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

-Xnoclassgc: HotSpot虚拟机默认进行无用类的垃圾回收,设置这个参数表示不进行类的垃圾回收。

3.3 垃圾收集算法
3.3.1 标记-清除算法
 标记-清除:Mark-Sweep;标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
 缺点:
 效率问题,标记和清除过程的效率都不高
 空间问题,产生大量不连续的内存碎片。

3.3.2 复制算法
将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块内存用完,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

新生代的回收即采用复制算法。
新生代内存包括一块较大的Eden空间和两块较小的Survivor空间。

3.3.3 标记-整理算法
这是老年代的垃圾收集算法。

3.3.4 分代收集算法
把Java堆分为新生代和老年代,根据各个年代的特点采用最适当的收集算法。
3.4 HotSpot的算法实现
3.4.1 枚举根节点
为什么必须要有GC停顿?P72
3.4.2 安全点
3.4.3 安全区域
3.5 垃圾收集器
HotSpot 1.6版Update 22的收集器列表:

3.5.1 Serial收集器
最基本、历史最悠久的收集器
必须暂停七天所有的工作线程,直到它收集结束。
是虚拟机运行在Client模式下的默认新生代收集器。

3.5.2 ParNew收集器
 Serial收集器的多线程版本
 为什么会有这个收集器?一个原因是:除了Serial收集器外,只有它能与CMS收集器配合工作。
并行(Parallel): 多条垃圾收集器线程并行工作,但此时用户线程仍处于等待状态。
并发(Concurrent): 用户线程与垃圾收集线程同时执行,用户程序继续运行,垃圾收集程序运行于另一个CPU上。

 -XX:+UseConcMarkSweepGC:表示使用CMS收集器,如果配置了这个,默认的新生代收集器是ParNew
 -XX:+UseParNewGC:指定新生代收集器是ParNew
 -XX:ParallelGCThreads:默认开启的收集线程数与CPU的数量相同,在CPU非常多时,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数

3.5.3 Parallel Scavenge收集器
 CMS收集器:尽可能缩短垃圾收集时用户线程的停顿时间;Parallel Scavenge收集器的目标是达到一个可控制的吞吐量(Throughput)
 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
 -XX:+UseParallelGC:JVM运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old的收集器组合进行回收
 -XX:MaxGCPauseMillis:控制最大垃圾收集停顿时间,大于0的毫秒值 (注:一般不设置)
 -XX:GCTimeRatio:设置吞吐量大小;如果把此参数设置为19,那允许的最大GC时间=1/(1+19)=5%
 -XX:+UseAdaptiveSizePolicy(默认开启),虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整参数以提供最合适的停顿时间或最大吞吐量。(建议在使用Parallel Scavenge收集器时开启)
3.5.4 Serial Old收集器
 Serial收集器的老年代版本
 Client模式下的默认使用

3.5.5 Parallel Old收集器
 Parallel Scavenge的老年代版本

3.5.6 CMS收集器
 CMS(Concurrent Mark Sweep):以获取最短回收停顿时间为目标的收集器。
 一款优秀的收集器:并发收集,低停顿
 缺点:
 CMS收集器对CPU资源非常敏感。默认启动的回收线程数是(CPU梳理+3)/4,也就是当CPU在4个以上时,并发回收时垃圾收集线程最多占用不超过25%的CPU资源。但是当CPU不足4个时,那么CMS对用户程序的影响就可能变得很大。
 无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。 浮动垃圾:…;默认设置下,CMS收集器在老年代使用了68%的空间后就会被激活,这偏保守了,可以调高参数-XX:CMSInitiatingOccupancyFraction的值来提高触发百分比,以降低内存回收次数以获取更好的性能。当然设置太高也有问题…
 产品大量空间碎片。为了解决这个问题,CMS收集器提供了一个-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外附送一个碎片整理过程,内存整理的过程是无法并发的。-XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的
 整个过程分4步:
 初始标记(CMS initial mark)
 并发标记(CMS concurrent mark)
 重新标记(CMS remark)
 并发清除(CMS concurrent sweep)

3.5.7 G1收集器
 在JDK1.7里已经实现商用,目标是在将来替换CMS收集器。
 特点:
 并行与并发
 分代收集
 空间整合
 可预测的停顿

3.5.8 理解GC日志
33.125: [GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs]
100.667: [Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->210K(19456K), [Perm : 2999K->2999K(21248K)], 0.0150007 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]

 33.125和100.667:GC发生的时间,从Java虚拟机启动以来经过的秒数。
 [GC 和 [Full GC: 垃圾收集的停顿类型,而不是用来区分新生代GC还是老年代GC的。如果有“Full”,说明这次GC是发生了Stop-The-World的,例如下面这段新生代收集器ParNew的日志也会出现“[Full GC”(这一般是因为出现了分配担保失败之类的问题,所以才导致STW)。如果是调用System.gc()方法所触发的收集,那么在这里将显示“[Full GC (System)”。
 接下来的“[DefNew”、“[Tenured”、“[Perm”表示GC发生的区域,这里显示的区域名称与使用的GC收集器是密切相关的
 [DefNew:Default New Generation,缺省新生代,[Tenured:年老代,[Perm:永久代
 [ParNew:Parallel New Generation,Parallel新生代
 [PSYoungGen:Parallel Scavenge收集器
 [Tenured:年老代
3.5.9 垃圾收集器参数总结
参数 描述
UseSerialGC 虚拟机运行在Client模式下的默认值,打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收
UseParNewGC 打开此开关后,使用ParNew + Serial Old的收集器组合进行内存回收
UseConcMarkSweepGC 打开此开关后,使用ParNew + CMS + Serial Old的收集器组合进行内存回收。Serial Old收集器将作为CMS收集器出现Concurrent Mode Failure失败后的后备收集器使用
UseParallelGC 虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old(PS MarkSweep)的收集器组合进行内存回收
UseParallelOldGC 打开此开关后,使用Parallel Scavenge + Parallel Old的收集器组合进行内存回收
SurvivorRatio 新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden∶Survivor=8∶1
PretenureSizeThreshold 直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配
MaxTenuringThreshold 晋升到老年代的对象年龄。每个对象在坚持过一次Minor GC之后,年龄就增加1,当超过这个参数值时就进入老年代
UseAdaptiveSizePolicy 动态调整Java堆中各个区域的大小以及进入老年代的年龄
HandlePromotionFailure 是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survivor区的所有对象都存活的极端情况
ParallelGCThreads 设置并行GC时进行内存回收的线程数
GCTimeRatio GC时间占总时间的比率,默认值为99,即允许1%的GC时间。仅在使用Parallel Scavenge收集器时生效
MaxGCPauseMillis 设置GC的最大停顿时间。仅在使用Parallel Scavenge收集器时生效
CMSInitiatingOccupancyFraction 设置CMS收集器在老年代空间被使用多少后触发垃圾收集。默认值为68%,仅在使用CMS收集器时生效
UseCMSCompactAtFullCollection 设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理。仅在使用CMS收集器时生效
CMSFullGCsBeforeCompaction 设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS收集器时生效
3.6 内存分配与回收策略
3.6.1 对象优先在Eden分配
 当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
 -XX:+PrintGCDetails:虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出的时候输出当前的内存各区域分配情况
 Minor GC:新生代GC,指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。
 Major GC/Full GC:指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。
 -verbose:gc 表示输出虚拟机中GC的详细情况
3.6.2 大对象直接进入老年代
 -XX:PretenureSizeThreshold,令大于这个设置值的对象直接在老年代分配。
示例:代码清单3-4 大对象直接进入老年代

3.6.3 长期存活的对象将进入老年代
 虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。对象在Survivor区中每“熬过”一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

示例:代码清单3-5 长期存活的对象进入老年代

3.6.4 动态对象年龄判定
 如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
3.6.5 空间分配担保
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。

4 虚拟机性能监控与故障处理工具
4.1 概述
4.2 JDK的命令行工具
对于JDK1.5,添加参数“-Dcom.sun.management.jmxremote”开启JMX管理功能

常用的: jps, jstat, jinfo, jmap, jstack
4.2.1 jps: 虚拟机进程状况工具
jps: 可以列出正在运行的虚拟机进程(jvm ps)
格式:jsp [options] [hosted]
示例:jsp –l

4.2.2 jstat: 虚拟机统计信息监视工具
jstat: JVM Statistics Monitoring Tool,用于监视虚拟机各种运行状态信息的命令行工具。
命令格式:jstat [option vmid [interval[s|ms] [count]]]
本地虚拟机:VMID和LVMID一致
远程虚拟机:VMID格式:[protocol:][//]lvmid[@hostname[:port]/servername]
Interval:查询间隔
Count: 次数

示例: 每250毫秒查询一次进程2764垃圾收集状况,查询20次
jstat –gc 2764 250 20

4.2.3 jinfo: Java配置信息工具
jinfo: Configuration Info for Java,实时查看和调整虚拟机各项参数
建议:使用java -XX:+PrintFlagsFinal查看参数默认值
4.2.4 jmap: Java内存映像工具
 jmap: Memory Map for Java),生成堆转储快照dump文件
 -XX:+HeapDumpOnOutOfMemoryError
 -XX:+HeapDumpOnCtrlBreak,使用[Ctrl]+[Break]键生成dump文件
 格式:jmap [option] vmid

4.2.5 jhat: 虚拟机堆转储快照分析工具(不用)
这个太原始,一般不用

4.2.6 jstack: Java堆栈跟踪工具
 jsack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照
 建议利用java.lang.Thread的getAllStackTraces()方法来获取虚拟机中所有线程的StackTraceElement对象。

jstack [option] vmid

4.2.7 HSDIS: JIT生成代码反汇编
不怎么用
4.3 JDK的可视化工具
4.3.1 JConsole: Java监视与管理控制台
4.3.1.1 启动JConsole
4.3.1.2 内存监控
相当于可视化的jstat
4.3.1.3 线程监控
相当于可视化的jstack
4.3.2 VisualVM: 多合一故障处理工具
4.3.2.1 VisualVM兼容范围与插件安装
4.3.2.2 生成、浏览堆转储快照
4.3.2.3 分析程序性能
 生成环境下不要使用,有比较大的性能影响。
 可能有bug,最好被监控程序中使用-Xshare:off参数来关闭类共享优化。
4.3.2.4 BTrace动态日志跟踪
相关资料见:https://kenai.com/projects/btrace/pages/Home
5 调优案例分析与实战
5.1 概述
5.2 案例分析
5.2.1 高性能硬件上的程序部署策略
5.2.2 集群间同步导致的内存溢出
5.2.3 堆外内存导致的溢出错误
5.2.4 外部命令导致系统缓慢
5.2.5 服务器JVM进程崩溃
5.2.6 不恰当数据结构导致内存占用过大
5.2.7 由Windows虚拟内存导致的长时间停顿
5.3 实战:Eclipse运行速度调优
 1.5升级为1.6,这个过程有可能产生持久代内存溢出,需要调整eclipse.ini

 1.6的类加载时间过长,差不多是1.5的两倍,可以通过参数-Xverify:none来禁止字节码验证过程
 减少Full GC

 屏蔽System.gc(): -XX:+DisableExplicitGC
 选择收集器降低延迟:-XX:+UseConcMarkSweepGC –XX:+UseParNewGC

最终配置结果:

6 类文件结构
6.1 Class类文件的结构
Class文件格式:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}

6.1.1 魔数与Class文件的版本
1-4字节:0xCAFEBABE
5-6字节:次版本号
7-8字节:主版本号

6.1.2 常量池
 字面量(Literal):Java中的常量
 符号引用(Symbolic References):包括三类常量:
 类和接口的全限定名(Fully Qualified Name)
 字段的名称和描述符(Descriptor)
 方法的名称和描述符

常量池的每一项常量都是一个表,共有11种不同的表结构,表开始的第一位是一个u1类型的标志位

CONSTANT_Class_info型常量的结构:

name_index是一个索引值,它指向常量池中一个CONSTANT_Utf8_info类型的常量

CONSTANT_Utf8_info类型的结构:

使用javap查看字节码的内容:
javap –verbose TestClass

6.1.3 访问标志

6.1.4 类索引、父类索引与接口索引集合

6.1.5 字段表集合

name_index: 字段的简单名称;如:一个类的inc()方法和m字段的简单名称分别是“inc”和“m”
descriptor_index: 字段和方法的描述符;

对于数组类型:。。。
6.1.6 方法表集合

6.1.7 属性表集合

6.1.7.1 Code属性

 attribute_name_index: 对应固定值“Code”
 attribute_length: 属性值的长度,属性表长度-6
 max_stack: 操作数栈深度最大值
 max_locals: 局部变量表所需的存储空间,单位是Slot
 code:存储字节码指令的一系列字节流(字节码指令)

 exception_table:
结构:

6.1.7.2 Exceptions属性
Exceptions属性的作用是列举出方法中可能抛出的受查异常(Checked Exception)

6.1.7.3 LineNumberTable属性
用于描述Java源码行号与字节码行号之间的对应关系。

6.1.7.4 LocalVariableTable属性
用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。

name_index: 局部变量的名称
descriptor_index: 该具备变量的描述符

6.1.7.5 SourceFile属性
用于记录生成这个Class文件的源码文件名称。

6.1.7.6 ConstantValue属性

6.1.7.7 InnerClasses属性
用于记录内部类与宿主类之间的关联

6.1.7.8 Deprecated及Synthetic属性

其中,attribute_length数据项的值必须为0x00000000,因为没有任何属性值需要设置

6.2 Class文件结构的发展

7 虚拟机类加载机制
7.1 概述

7.2 类加载的时机

有且只有四种情况必须立即对类进行初始化:
 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时
 使用java.lang.reflet包的方法对类进行反射调用的时候
 当初始化一个类的时候,发现其父类还没有初始化
 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类

7.3 类加载的过程
全过程:加载、验证、准备、解析和初始化
7.3.1 加载
 通过一个类的全限定名来获取定义此类的二进制字节流
 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。

7.3.2 验证
连接阶段的第一步,目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
7.3.2.1 文件格式验证
验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。
7.3.2.2 元数据验证
对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。
7.3.2.3 字节码验证
保证被校验类的方法在运行时不会做出危害虚拟机安全的行为。
7.3.2.4 符号引用验证
对类自身以外(常量池中的各种符号引用)的信息进行匹配性的校验。

7.3.3 准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。

public static int value=123;

变量value在准备阶段过后的初始值为0而不是123。

7.3.4 解析
虚拟机将常量池内的符号引用替换为直接引用的过程。
解析动作主要针对类或接口、字段、类方法、接口方法四类符号引用进行,分别对应于常量池的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info及CONSTANT_InterfaceMethodref_info四种常量类型。
7.3.4.1 类或接口的解析
把一个从未解析过的符号引用N解析为一个类或接口C的直接引用:

7.3.4.2 字段解析

7.3.4.3 类方法解析

7.3.4.4 接口方法解析

7.3.5 初始化
类加载过程的最后一步,是执行类构造器()方法的过程

7.4 类加载器
作用:通过一个类的全限定名来获取描述此类的二进制字节流

7.4.1 类和类加载器

7.4.2 双亲委派模型
JVM角度:两种不同的类加载器:启动类加载器,其他的类加载器
开发人员角度:…

7.4.3 破坏双亲委派模型

8 虚拟机字节码执行引擎
8.1 概述

8.2 运行时栈帧结构
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素。
个人理解:一个栈帧对应一个方法调用
概念:每个线程拥有自己独立的堆栈

8.2.1 局部变量表
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。

8.2.2 操作数栈
操作数栈的最大深度在编译时写入Code属性的max_stacks数据项中。
8.2.3 动态连接

8.2.4 方法返回地址

8.2.5 附加信息

8.3 方法调用
方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。
8.3.1 解析
类加载阶段,会将其中一部分符号引用转化为直接引用

8.3.2 分派
对Java的多态特征,虚拟机是如何确定正确的目标方法的?
8.3.2.1 静态分派
Human man=new Man();
这里”Human”称为变量的静态类型(Static Type)或者外观类型,后面的”Man”则成为变量的实际类型(Actual Type)。
Java的重载分派基于静态类型进行分派。
8.3.2.2 动态分派
Java的重写分派是基于动态分派的。
注:重写那是可以添加注解@overirde的

8.3.2.3 单分派与多分派
根据分派基于多少种宗量,可以将分派划分为单分派和多分派两种。单分派是根据一个宗量对目标方法进行选择,多分派则是根据多于一个的宗量对目标方法进行选择。

8.3.2.4 虚拟机动态分派的实现

8.4 基于栈的字节码解释执行引擎
8.4.1 解释执行

8.4.2 基于栈的指令集与基于寄存器的指令集

8.4.3 基于栈的解释器执行过程

9 类加载及执行子系统的案例与实战
9.1 概述

9.2 案例分析
9.2.1 Tomcat: 正统的类加载器架构

9.2.2 OSGi: 灵活的类加载器架构

9.2.3 字节码生成技术与动态代理的实现

9.2.4 Retrotranslator: 跨越JDK版本
http://retrotranslator.sourceforge.net/

9.3 实战:自己动手实现远程执行功能
9.3.1 目标
9.3.2 思路

9.3.3 实现

10 早期(编译器)优化
10.1 概述
 前端编译器:Sun的Javac、Eclipse JDT中的增量式编译器(ECJ)
 JIT编译器:HotSpot VM的C1、C2编译器。
 AOT编译器:GNU Compiler for the Java(GCJ)、Excelsior JET
我们所说的编译期专指第一类编译过程。
10.2 Javac编译器
10.2.1 Javac的源码与调试
源码存放在(OpenJDK)JDK_SRC_HOME/langtools/src/share/classes/com/sun/tools/javac中

10.2.2 解析与填充符号表
解析步骤由图10-5中的parseFiles()方法(图中的过程1.1)完成,解析步骤包括了经典程序编译原理中的词法分析和语法分析两个过程。
10.2.2.1 词法、语法分析
词法分析:将源代码的字符流转变为标记(Token)集合。
语法分析:根据Token序列来构造抽象语法树的过程。
10.2.2.2 填充符号表
图10-5中的enterTrees()方法
10.2.3 注解处理器
插入式注解处理器的初始化过程是在initProcessAnnotations()方法中完成的,执行过程则在processAnnotations()方法中完成。
10.2.4 语义分析与字节码生成
10.2.4.1 标注检查
语义分析过程分为标注检查和数据及控制流分析两个步骤,分别由图10-5的attribute()和flow()方法(分别对应图中的过程3.1和过程3.2)完成。
10.2.4.2 数据及控制流分析

10.2.4.3 解语法糖

10.2.4.4 字节码生成

10.3 Java语法糖的味道
10.3.1 泛型与类型擦除

10.3.2 自动装箱、拆箱与遍历循环
注:包装类的“==”运算在没有遇到算术运算的情况下不会自动拆箱。

10.3.3 条件编译

10.4 实战:插入式注解处理器
10.4.1 实战目标
10.4.2 代码实现
11 晚期(运行期)优化
11.1 概述
11.2 HotSpot虚拟机内的即时编译器

11.2.1 解释器与编译器

11.2.2 编译对象与触发条件
方法调用计数器:

回边计数器

11.2.3 编译过程
Client Compiler

Server Compiler:

11.2.4 查看与分析即时编译结果

11.3 编译优化技术
以编译方式执行本地代码比解释方式更快。
11.3.1 优化技术概览
内联:许多优化手段都试图消除机器级跳转指令(例如,x86架构的JMP指令)。跳转指令会修改指令指针寄存器,因此而改变了执行流程。相比于其他汇编指令,跳转指令是一个代价高昂的指令,这也是为什么大多数优化手段会试图减少甚至是消除跳转指令。内联是一种家喻户晓而且好评如潮的优化手段,这是因为跳转指令代价高昂,而内联技术可以将经常调用的、具有不容入口地址的小方法整合到调用方法中。

11.3.2 公共子表达式消除

11.3.3 数组边界检查消除

11.3.4 方法内联

11.3.5 逃逸分析
如果能证明一个对象不会逃逸到方法或线程之外,也就是别的方法或线程无法通过任何途径访问到这个对象,则可能为这个变量进行一些高效的优化。
 栈上分配:
 同步消除:
 标量替换:

11.4 Java与C/C++的编译器对比

12 Java内存模型与线程
12.1 概述
TPS: Transactions Per Seconde,每秒事务处理数
12.2 硬件的效率与一致性

12.3 Java内存模型
12.3.1 主内存与工作内存

12.3.2 内存间交互操作
 八种基本操作:
lock
unlock
read
load
use
assign
store
write

 操作规则:

12.3.3 对于volatile型变量的特殊规则
volatile的两种特性:
 保证此变量对所有线程的可见性
 禁止指令重排序优化

12.3.4 对于long和double型变量的特殊规则
12.3.5 原则性、可见性和有序性
12.3.6 先行发生原则
 程序次序规则
 管理锁定规则
 Volatile变量规则
 线程启动规则
 线程中断规则
 对象终结规则
 传递性

12.4 Java与线程
12.4.1 线程的实现
12.4.1.1 使用内核线程实现
LWP:Light Weight Process,轻量级进程
KLT:Kernal Thread,内核线程

12.4.1.2 使用用户线程实现
UT: User Thread 用户线程

12.4.1.3 混合实现

12.4.1.4 Java线程的实现

12.4.2 Java线程调度
线程调度是指系统为线程分配处理器使用权的过程,主要调度方式有两种:协同式线程调度和抢占式线程调度。

12.4.3 状态转换

13 线程安全与锁优化
13.1 概述

13.2 线程安全
13.2.1 Java语言中的线程安全
13.2.1.1 不可变
13.2.1.2 绝对线程安全
13.2.1.3 相对线程安全
13.2.1.4 线程兼容
13.2.1.5 线程对立
13.2.2 线程安全的实现方法
13.2.2.1 互斥同步

13.2.2.2 非阻塞同步
通过硬件来保证一个语义上看起来需要多次操作的行为只通过一条处理器指令就能完成,常用的有:
 测试并设置(Test-and-Set)
 获取并增加(Fetch-an-Increment)
 交换(Swap)
 比较并交换(Compare-and-Swap,下文称CAS)
 加载链接/条件储存(Load-Linked/Store-Conditional,下文称LL/SC)
13.2.2.3 无同步方案
 可重入代码
 线程本地存储
13.3 锁优化
13.3.1 自旋锁与自适应自旋
13.3.2 锁消除
13.3.3 锁粗化
13.3.4 轻量级锁
HotSpot虚拟机的对象头(Object Header)分为两部分信息
 第一部分用于存储对象自身的运行时数据,如HashCode,GC分代年龄等,官方称为“Mark Word”。
 另外一部分用于存储指向方法区对象类型数据的指针,如果是数组对象的话,还会有一个额外的部分用于存储数组长度。
Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息。

13.3.5 偏向锁

14 附录
14.1 HotSpot虚拟机主要参数表
参数使用的方式有三种:
 -XX:+ 开启option参数
 -XX:- 关闭option参数
 -XX:= 将option参数的值设置为value
14.1.1 内存管理参数
14.2 对象查询语言(OQL)简介

15 其它
15.1 关于内存分配
32位windows有4G的寻址空间,有2G需要留给windows系统用,留给应用的只有2G。
32位Linux内存问题:参考:http://www.iteye.com/topic/368213

15.2 其它
可以直接生成本地代码的编译器(如GCJ,GNU compiler for the Java)
一个问题:使用wait,线程数会变少吗?
Synchronized与读锁比的性能呢?
如何监控虚拟机栈?
可以研究类的垃圾收集算法
如何默认client模式还是server模式? Java –version即可以查询到
Full GC?
如何测试client模式和server模式下色方法调用性能?
测试各种GC的性能问题
我们的通信平台到底是需要吞吐量优先还是低停顿优先?
需要熟悉VisualVM
需要学习BTrace:https://kenai.com/projects/btrace/pages/Home
针对5.2.6 不恰当数据结构导致内存占用过大,进行测试

相关参考资料:
http://taojingrui.iteye.com/blog/548828
http://blog.youkuaiyun.com/e5945/article/details/8641698
http://docs.oracle.com/javase/1.5.0/docs/guide/vm/gc-ergonomics.html
http://book.2cto.com/201306/25426.html

Docker小白也能搞定!5分钟用calibre-web搭建私人Kindle书库(附静读天下配置) 本文为零基础用户提供了一份详尽的指南,介绍如何利用Docker容器技术,在5分钟内快速部署Calibre-Web服务,从而搭建一个完全私有的个人Kindle书库。教程涵盖了从Docker安装、Calibre-Web一键部署、书籍元数据管理到移动端配置的全流程,并重点讲解了如何与安卓端知名阅读应用“静读天下”联动,实现跨设备无缝访问,打造媲美在线书城的个性化阅读体验。 阅读详情

相关推荐

Win10管理员权限的‘暗门’:从mmc.exe被阻止,聊聊UAC与自动提权那些事儿

本文深入解析Windows 10中UAC(用户账户控制)与自动提权机制,探讨mmc.exe被阻止的深层原因及解决方案。从UAC工作原理到自动提权机制,再到具体诊断与修复步骤,帮助用户理解并解决权限管理问题,同时平衡安全性与便利性。

weixin_30521161的博客 418

java 查看内存对象的大小

maven: <!-- https://mvnrepository.com/artifact/com.carrotsearch/java-sizeof --> <dependency> <groupId>com.carrotsearch</groupId> <artifactId>java-sizeof</artifactId> <version>0.0.5</version>

我惠依旧的博客 1170

深入解析AndroidManifest.xml与device_admin.xml:打造完美DeviceOwner应用配置

本文深入解析了Android企业级开发中DeviceOwner应用的核心配置文件AndroidManifest.xml与device_admin.xml。详细阐述了如何精准声明DeviceAdminReceiver、定义管理策略,并提供了通过ADB命令进行设置与验证的实战指南,帮助开发者构建权限完整、配置正确的设备所有者应用。

ice55的博客 679

jps 命令详解

JPS 名称:jps - Java Virtual Machine Process Status Tool 命令用法:jps [options] [hostid] options:命令选项,用来对输出格式进行控制 hostid:指定特定主机,可以是ip地址和域名, 也可以指定具体协议,端口。 [prot...

极客on之路 2201

树莓派与ROS2

树莓派与ROS2(Robot Operating System 2)的结合使用为机器人开发、自动化控制以及物联网项目提供了强大的平台。

weixin_47695827的博客 1754

01|无代码开发 | 如何使用Zion实现导航网站制作

以上就是我制作导航网站的心得,分享给大家,作为一个小白想要独立搭建一个网站还是比较困难的,而在Zion不仅可以用可视化的方法进行搭建,甚至还可以一键发布,不用考虑服务器域名啥的问题,真是太方便了。

2301_76262110的博客 1390

《深入理解java虚拟机》读书笔记 第七章 虚拟机类加载机制

概述 1. 虚拟机把描述类的数据从Class文件加载到内存 2. 对数据进行校验、转换解析、初始化 3. 最终形成可被虚拟机直接使用的java类型 类加载的时机 "类从被虚拟机加载到内存,到卸载出内存的生命周期": 加载(Loading) 验证(Verification) 准备(Preparation) 解析(Resolution) 初始化(Initial...

lik_lik的博客 1359

【光子AI / Photon.AI】manus 通用 Agent 的实现核心技术原理与应用

摘要 Manus是一款由国内团队开发的通用AI Agent,其核心技术原理基于多智能体协作架构、云端全能沙箱、规划-执行-反思循环等系统工程方法。与传统的对话式AI不同,Manus能够在独立的Linux虚拟机环境中自主规划并执行复杂任务,通过编写和运行代码来解决问题,具备强大的纠错能力和工具使用能力。 核心应用场景包括深度行业研究、自动化办公、生活助理、辅助编程和跨平台信息整合等。Manus的工程化落地优势体现在沙箱环境、多智能体调度和容错机制等方面,使其能够完成需要多步骤操作和多种工具调用的复杂任务,将A

AI天才研究院 666

Jps命令—使用详解

jps是jdk提供的一个查看当前java进程的小工具, 可以看做是JavaVirtual Machine Process Status Tool的缩写。非常简单实用。 命令格式:jps [options ] [ hostid ]  [options]选项 : -q:仅输出VM标识符,不包括classname,jar name,arguments in main method  -m:输出m

wisgood的专栏 8万+

硬核反坑!YOLOv5训练避坑指南:根治梯度消失、不收敛、检测漏框三大核心问题(附实战代码+调优清单)

数据是基础,超参数是关键,模型是工具,训练策略是保障。梯度消失、不收敛、检测漏框这三大问题,本质上都是“数据-模型-训练”不匹配的结果。梯度消失/爆炸:优先调整学习率策略+梯度裁剪,其次优化模型结构;训练不收敛:优先清洗数据集,其次调优超参数,最后检查配置文件;检测漏框:优先重新聚类锚框,其次增强浅层特征,最后调整推理参数。YOLOv5训练的最高境界,不是调参,而是让数据、模型、训练策略三者完美匹配。按本文的方法反坑,你的YOLOv5训练一定能一次成功!🚀。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 232

下载MNIST数据集并使用python将数据转换成NumPy数组(源码解析)

下载MNIST数据集并使用python将数据转换成NumPy数组首先来分析init_mnist函数接下来继续分析load_mnist函数实现数据集转换的python脚本的代码显示MNIST图像并确认数据 下载MNIST数据集并将数据转换成NumPy数组的Python脚本里面最重要的就是load_mnist函数,其他项目想要调用数据集的话,就可以调用load_mnist函数,得到一个字典类型的数据,字典的值是一个Numpy数组。 这些过程是如何实现的,现在开始逐字逐句分析源码: 在load_mnist函数中第

及时总结 4266

[深入理解Java虚拟机]第三章 理解GC曰志

阅读GC日志是处理Java虚拟机内存问题的基础技能,它只是一些人为确定的规则,没有太多技术含量。每一种收集器的日志形式都是由它们自身的实现所决定的,换而言之,每个收集器的曰志格式都可以不一样。但虚拟机设计者为了方便用户阅读,将各个收集器的日志都维持一定的共性,例如以下两段典型的GC日志:33.125:[GC[DefNew:3324K->152K(3712K),0.0025925 secs]3324K

Code-lover's Learning Notes 658

SAP CPI 和 PO 的区别

SAP CPI适合云集成轻量级的集成需求,并且由于其云架构,具有快速部署低维护高度可扩展的特点。SAP PO更适合本地系统集成复杂的集成场景和高度定制化的需求,并且对于已经有 SAP 基础设施的企业来说,PO 提供了更多的控制和灵活性。选择使用哪个平台通常取决于企业的技术架构、集成需求、预算以及对灵活性和控制的需求。如果企业主要使用 SAP 的本地系统,并且有复杂的集成需求,那么 SAP PO 是一个理想的选择。如果企业更多地依赖云应用,并希望简化集成操作,那么 SAP CPI 更加适合。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1869

《深入理解Java虚拟机》导读

​ 简介: ​ 介绍初学者如何快速学习阅读《深入理解Java虚拟机》书籍。 书籍PDF:深入理解Java虚拟机:(第3版).pdf · UITC/save-paper - Gitee.com.pdf)

yzy_cb的博客 525

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

深入理解java虚拟机

深入理解java虚拟机笔记文档JVM运行时数据区(JVM Runtime Area)

zzz的博客 277

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

《Thinking In Java》 VS 《Effective Java》 VS 《深入理解Java虚拟机》

今天刚考完试,在5天以前的前4个月时间里,我很庆幸自己选了一个看书的学习途径。从上个学期的暑假到现学期的11周里面,读了《Thinking In Java》、《深入理解Java虚拟机》和《Effective Java》,感悟颇多。首先先说明一下,我以前是看视频学习的,就做过两三个学校的网站,都已经上线在使用。没做过什么特别高大上的东西,所以我是处在新手的角度去读书的,我也知道自己的理解很浅,不

小浩子的博客 2万+

深入理解Java虚拟机总结

花了几天学习完了周志明老师写的《深入理解Java虚拟机–JVM高级特性与最佳实践》一书,为更加深入的理解以及日后复习之用,写一篇总结

demonwang1025的博客 3738

深入理解Java虚拟机第三版来袭

Java是目前最流行的语言之一,作为一个Java工程师更应该深入了解一下Java虚拟机,而支持Java程序运行的虚拟机资料并不多。当时在网上没有找到什么资源,直到我看到周志明老师的《深入理解Java虚拟机》,这简直是Java程序员的福音。第一次见到这本书是在同事的桌上,我顺手拿起来读了前两章,让我一直感觉非常神秘的虚拟机,终于被解开了面纱。之后便被深深地吸引,自己也入手一本,开始了学习之路。 周志...

专注的博客 1万+

《深入理解Java虚拟机》读后感

一、本书印象: 大概是入行半年后有所闻名,对于本书的相关概念很模糊,大概只有对虚拟机中的GC垃圾回收有一点印象。真正开始接触本书也是入行八九个月,因为入行半年多换了个工作,从那时起几乎所有的技术面试都有关于虚拟机的相关概念知识的提问,从此惦记在心:在新工作岗位有空闲时间必须掌握这门技术知识! 第一次接触的是电子档,其实有两本近似的入门参考资料,选择周志明的《深...

weixin_42683814的博客 579

《深入理解Java虚拟机》提纲

这两天重温了下周志明大神的《深入理解Java虚拟机》第二版,真是经典中的经典,每次看都有新的收获,画了下我理解的每个章节在整个流程中的位置,括弧中的数字对应书中的章节,有问题欢迎指正。 ...

SjtuDarcy的博客 213

深入理解Java虚拟机-走近Java

文章目录概述Java技术体系Java发展史Java虚拟机发展史展望Java技术的未来实战:自己编译JDK本章小结 本博客主要参考周志明老师的《深入理解Java虚拟机》第二版 读书是一种跟大神的交流。阅读《深入理解Java虚拟机》受益匪浅,对Java虚拟机有初步的认识。这里写博客主要出于以下三个目的:一方面是记录,方便日后阅读;一方面是加深对内容的理解;一方面是分享给大家,希望对大家有帮助。 ...

ThinkWon的博客 2万+

深入理解java虚拟机 下载_深入理解Java虚拟机

深入理解Java虚拟机电子版深入理解Java虚拟机电子版,全书共分为五大部分,统介绍了Java的技术体系、发展历程、虚拟机家族,以及动手编译JDK,了解这部分内容能对学习JVM提供良好的指引,里面还有更多的精彩内容,感兴趣的就快来下载吧内容简介《深入理解Java虚拟机:JVM高级特性与最佳实践》内容简介:作为一位Java程序员,你是否也曾经想深入理解Java虚拟机,但是却被它的复杂和深奥拒之门外?...

weixin_28831399的博客 838

深入理解JAVA虚拟机 脑图

深入理解JAVA虚拟机 最近读了《深入理解Java虚拟机》感觉,深深感受到了JAVA语言的强大。下面是整理的脑图,仅供参考。 如果看不清,链接【点击进入】

阿生 285

深入理解Java虚拟机读后感

大学专业就是计算机,后面大二开始就开始自己学习java的基本语法,自己写一些例子作为学习。后来参加工作也是以java开发为主,一直也没有深入的了解java是如何运行,也不太了解java的内存管理机制。大学课程学了C++,需要手动申请和释放内存,有 时忘记了就会导致内存泄露和溢出。学了java之后才发现原来java不用手动去做这些动作就可以自动管理内存,很好奇java是如何实现的。由于自己的英...

xiabaosen的专栏 445

jvm深入理解java虚拟机学习

各位java开发同学,在经过一段时间工作之后,可能对java虚拟机的原理还不甚了解。只是停留在使用的层面,为了更好的写出优雅高效的代码,增加自己的技术深度。经过努力收集,找到了2019年12月才出版的 《深入理解java虚拟机》第三版。供有需要的同学学习。收集资料不易,谢谢大家支持。 百度网盘地址:链接:https://pan.baidu.com/s/1JQsMv--LkwtmQJjAu0l-G...

a274433141的博客 172

1000页巨著,《深入理解Android:Java虚拟机ART》送15本

各位小伙伴们大家早上好,本来是计划在五一假期期间给大家做一次抽奖送书活动的,由于一些突发情况导致我这边的时间安排有些变化,所以就将抽奖活动提前到今天进行了。这次抽取的是由...

郭霖 2670
上一篇: java9终极特性
下一篇: dubbo介绍
HelloWorld_AA
博客等级 码龄9年 18粉丝 96原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值