JVM
→ JVM 内存结构
运行时数据区:堆、栈、方法区、直接内存、运行时常量池、
堆存放对象, 方法区他用于存储已被虚拟机加载的类信息 两个区域是共享区域
栈 线程私有 用于存放 程序操作数临时变量等
直接内存
直接内存并不是虚拟机运行内存的一部分,也不是java虚拟机规范中定义的内存区域。但是这部分内存区域也被频繁的使用,也可能导致OutOfMemoryError异常出现,
在jdk1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)和缓冲区(Buffer)的I/O方式,他可以使用本地的函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,这样能在一些场景中显著提高性能,因为避免了在java堆中和Native堆中来回复制数据。
显然本机直接内存的分配不会受到java堆大小的限制,但是既然是内存。肯定还会受到本机总内存的限制。服务器管理员在配置虚拟机内存参数时,会根据实际内存设置-Xmx等参数信息。但经常忽略直接内存,使得各个内存区域总和大于物理内存限制(包括物理和操作系统级的限制),从而导致动态扩展时OutOfMemoryError
运行时常量池
方法区的一部分,所有线程共享。虚拟机加载class文件后把常量池中的数据存放到运行时常量池中
堆和栈区别
栈(操作系统):由操作系统(编译器)自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
2、堆栈缓存方式区别
栈使用的是一级缓存, 它们通常都是被调用时处于存储空间中,调用完毕立即释放。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
3、堆栈数据结构区别
堆(数据结构):堆可以被看成是一棵树,如:堆排序。
栈(数据结构):一种先进后出的数据结构。
Java 中的对象一定在堆上分配吗?
不是的 https://blog.csdn.net/dgxin_605/article/details/93754012
→ Java 内存模型
[https://www.cnblogs.com/YJK923/p/10478716.html] 引用博客
这里做一下总结;
Java 内存模型中规定了所有的变量都存储在主内存中,每个线程还有自己的工作内存(类比缓存理解),线程的工作内存中保存了该线程使用到主内存中的变量拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递(通信)均需要在主内存来完成.
jmm如何保证原子性,可见性、有序性
原子性 通过synchronized 进行保证,字节码层面 monitorenter 和 monitorexit 来保证的。
可见性 通过synchronized和volatile 保证
有序性volatile 防止指令重排和 happens-before 原则保证有序性
缓存一致性、MESI 协议
MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态
→ 垃圾回收
GC 算法:标记清除、引用计数、复制、标记压缩、分代回收、增量式回收
GC 参数、对象存活的判定、垃圾收集器(CMS、G1、ZGC、Epsilon)
cms 垃圾收集器
最短回收时间的收集器,基于标记清除算法
优点 并发收集 低停顿
缺点 产生大量的空间碎片 并发阶段降低吞吐量
G1
标记整理算法
空间整合 和可预测停顿是两个特点
→ JVM 参数及调优
-Xmx、指定最大堆
-Xmn、堆内新生代的大小。通过这个值也可以得到老生代的大小
-Xms、指定最小堆Xss、
-XX:SurvivorRatio、新生代中Eden区域和Survivor区域比例
-XX:PermSize、表示非堆区初始内存分配大小,其缩写为permanent size(持久化内存)
-XX:MaxPermSize、表示对非堆区分配的内存的最大上限。
-XX:MaxTenuringThreshold 在新生代中对象存活次数(经过Minor GC的次数)后仍然存活,就会晋升到旧生代。
→ HotSpot
即时编译器、编译优化
→ 虚拟机性能监控与故障处理工具
jps, 查看java进程信息
jstack,导出线程堆栈信息
jmap, dump出内存快照信息。内存异常使用
jstat,虚拟机运行时参数 可用于查看gc日志等
jconsole,jvm监控工具
jinfo,系统参数等
jhat,分析jmap结果
javap,反编译
btrace, Btrace (Byte Trace)是sun推出的一款java 动态、安全追踪工具,可以不停机的情况下监控线上情况,并且做到最少的侵入,占用最少的系统资源。BTrace应用较为广泛的原因应该是其安全性和无侵入性,其中热交互技术,使得我们无需启动Agent的情况下动态跟踪分析,其安全性不会导致对目标Java进程的任何破坏性影响,使得BTrace成为我们线上产品问题定位的利器。无侵入性无需我们对原有代码做任何修改,降低上线风险和测试成本,并且无需重启启动目标Java进程进行Agent加载即可动态分析和跟踪目标程序,可以说BTrace可以满足大部分的应用场景。
TProfiler 分析代码执行次数时间,做调优时可以使用 其他场景后续补充
Arthas 阿里巴巴诊断工具
02
类加载机制
双亲委派
classLoader、

BootstrapClassLoader(启动类加载器)
负责加载 JVM 运行时核心类,加载System.getProperty("sun.boot.class.path")所指定的路径或jar
类加载过程、
ExtensionClassLoader
负责加载 JVM 扩展类,比如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头,它们的 jar 包位于 JAVAHOME/lib/rt.jar文件中.
加载System.getProperty("java.ext.dirs")所指定的路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld。
AppClassLoader
才是直接面向我们用户的加载器,它会加载 Classpath 环境变量里定义的路径中的 jar 包和目录。我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的。
加载System.getProperty("java.class.path")所指定的路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classes Hello
双亲委派(破坏双亲委派)、
双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。这里的双亲其实就指的是父类,没有mother。父类也不是我们平日所说的那种继承关系,只是调用逻辑是这样。
双亲委派模型不是一种强制性约束,也就是你不这么做也不会报错怎样的,它是一种JAVA设计者推荐使用类加载器的方式。
双亲委派有啥好处呢?它使得类有了层次的划分。就拿java.lang.Object来说,你加载它经过一层层委托最终是由Bootstrap ClassLoader来加载的,也就是最终都是由Bootstrap ClassLoader去找<JAVA_HOME>\lib中rt.jar里面的java.lang.Object加载到JVM中。
这样如果有不法分子自己造了个java.lang.Object,里面嵌了不好的代码,如果我们是按照双亲委派模型来实现的话,最终加载到JVM中的只会是我们rt.jar里面的东西,也就是这些核心的基础类代码得到了保护。因为这个机制使得系统中只会出现一个java.lang.Object。不会乱套了。你想想如果我们JVM里面有两个Object,那岂不是天下大乱了。
因此既然推荐使用这种模型当然是有道理了。
但是人生不如意事十之八九,有些情况不得不违反这个约束,例如JDBC。
你先得知道SPI(Service Provider Interface),这玩意和API不一样,它是面向拓展的,也就是我定义了这个SPI,具体如何实现由扩展者实现。我就是定了个规矩。
JDBC就是如此,在rt里面定义了这个SPI,那mysql有mysql的jdbc实现,oracle有oracle的jdbc实现,反正我java不管你内部如何实现的,反正你们都得统一按我这个来,这样我们java开发者才能容易的调用数据库操作。
所以因为这样那就不得不违反这个约束啊,Bootstrap ClassLoader就得委托子类来加载数据库厂商们提供的具体实现。因为它的手只能摸到<JAVA_HOME>\lib中,其他的它无能为力。这就违反了自下而上的委托机制了。
Java就搞了个线程上下文类加载器,通过setContextClassLoader()默认情况就是应用程序类加载器然后Thread.current.currentThread().getContextClassLoader()获得类加载器来加载。
模块化(jboss modules、osgi、jigsaw)待补充
03
编译与反编译
什么是编译(前端编译、后端编译)、什么是反编译
JIT、JIT 优化(逃逸分析、栈上分配、标量替换、锁优化)
编译工具:javac
反编译工具:javap 、jad 、CRF
网友评论