常州网站开发培训,网站设计太原,店面招牌设计效果图大全,wordpress 访问控制目录 一、类加载器
jvm类的加载过程
第一阶段#xff1a;加载
第二阶段#xff1a;链接阶段
第三阶段#xff1a;初始化阶段#xff1a;
双亲委派机制
沙箱安全机制
运行时数据区
栈-Xss1m
堆
TLAB
逃逸分析
方法区
常量池中有什么
StringTable为什么要调整位…目录 一、类加载器
jvm类的加载过程
第一阶段加载
第二阶段链接阶段
第三阶段初始化阶段
双亲委派机制
沙箱安全机制
运行时数据区
栈-Xss1m
堆
TLAB
逃逸分析
方法区
常量池中有什么
StringTable为什么要调整位置
静态变量存放在那里
对象实例化内存布局与访问定位
运行时元数据
类型指针
实例数据Instance Data
对齐填充说明
StringTable
注意
字符串拼接操作 一、类加载器
jvm类的加载过程 第一阶段加载
通过一个类的全限定名获取定义此类的二进制字节流
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
在内存中生成一个代表这个类的java.lang.Class对象作为方法区这个类的各种数据的访问入口
第二阶段链接阶段
验证 文件格式 元数据验证 字节码验证 符号引用验证
准备为类变量分配内存 设置默认值
解析 符号引用转换为直接引用
事实上解析操作往往会伴随着JVM在执行完初始化之后再执行。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT Class info、CONSTANT Fieldref info、CONSTANT Methodref info等
第三阶段初始化阶段
为静态变量赋予正确的初始值
执行类构造器 虚拟机必须保证一个类的clinit方法在多线程下被同步加锁。 从上面可以看出初始化后只能够执行一次初始化这也就是同步加锁的过程
双亲委派机制 方法 getParent()
loadClass() 双委派机制
findClass() 编写加载规则 将取得的字节码转换成流
defineClass() 生成类的class对象 protected Class? loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// 首先检查类是否已经加载Class? c findLoadedClass(name);if (c null) {long t0 System.nanoTime();try {if (parent ! null) {//类加载器有父类加载器,使用父类加载器加载c parent.loadClass(name, false);} else {//直到顶级类加载器即parent为空时由findBootstrapClassOrNull()方法尝试到Bootstrap ClassLoader中检查目标类。c findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c null) {// 如果仍未找到则调用findClass来查找该类。long t1 System.nanoTime();//通过findClass()方法尝试到对应的类目录下去加载目标类。c findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {//如果加载成功则根据resolve参数决定是否要执行连接过程并返回Class对象。resolveClass(c);}return c;}} Service Provider InterfaceSPI)的代码现在问题来了启动类加载器是绝不可能认识、加载这些代码的那该怎么办?(SPI:在Java平台中通常把核心类rt.jar中提供外部服务、可由应用层自行实现的接口称为SPI)
为了解决这个困境Java的设计团队只好引入了一个不太优雅的设计:线程上下文类加载器Thread Context ClassLoaden),这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置如果创建线程时还未设置它将会从父线程中继承一个如果在应用程序的全局范围内都没有设置过的话那这个类加载器默认就是应用程序类加载器。
JNDI服务使用这个线程上下文类加载器去加载所需的SPI服务代码,这是一种父类加载器去请求子类加载器完成类加载的行为,这种行为实际上是打通了双亲委派模型的层次结构来逆向使用类加载器已经违背了双亲委派模型的一般性原则。在JDK 6时,DK提供了java.util.ServiceLoader类以META-INF/services中的配置信息辅以责任链模式这才算是给SPI的加载提供了一种相对合理的解决方案。 Java虚拟机对class文件采用的是按需加载的方式 避免类的重复加载 保护程序安全防止核心API被随意篡改
沙箱安全机制
沙箱机制就是将Java代码限定在虚拟机(VN)特定的运行范围中并且严格限刺代码对本地系统资源访问。通过这样的措施来保证对代码的有限隔离,防止对本地系统造成破坏。
运行时数据区 栈-Xss1m 局部变量表Local Variables 操作数栈operand Stack或表达式栈 动态链接DynamicLinking或指向运行时常量池的方法引用 方法返回地址Return Address或方法正常退出或者异常退出的定义
每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接Dynamic Linking。比如invokedynamic指令
比如描述一个方法调用了另外的其他方法时就是通过常量池中指向方法的符号引用来表示的那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。
堆
堆可以处于物理上不连续的内存空间中但在逻辑上它应该被视为连续的。
所有的对象实例以及数组都应当在运行时分配在堆上。 “-Xms用于表示堆区的起始内存等价于-xx:InitialHeapSize “-Xmx则用于表示堆区的最大内存等价于-XX:MaxHeapSize
通常会将-Xms和-Xmx两个参数配置相同的值其目的是为了能够在ava垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小从而提高性能。 EdenFromto - 8:1:1 新生代老年代 - 1 : 2
几乎所有的Java对象都是在Eden区被new出来的。绝大部分的Java对象的销毁都在新生代进行了。有些大的对象在Eden区无法存储时候将直接进入老年代
YGC次数达到15次还存活的对象会移动到老年代。次数可以设置。默认是15次。 针对幸存者s0s1区的总结复制之后有交换谁空谁是to 关于垃圾回收频繁在新生区收集很少在老年代收集几乎不再永久代和元空间进行收集 新生代采用复制算法的目的是为了减少内碎片
部分收集不是完整收集整个Java堆的垃圾收集。其中又分为 新生代收集MinorGC/YoungGC只是新生代Eden\s0,s1的垃圾收集 老年代收集MajorGC/o1dGC只是老年代的圾收集。
TLAB
VM为每个线程分配了一个私有缓存区域它包含在Eden空间内。
多线程同时分配内存时使用TLAB可以避免一系列的非线程安全问题同时还能够提升内存分配的吞吐量因此我们可以将这种内存分配方式称之为快速分配策略。
逃逸分析
随着JIT编译期的发展与逃逸分析技术逐渐成熟栈上分配、标量替换优化技术将会导致一些微妙的变化所有的对象都分配到堆上也渐渐变得不那么“绝对”了。 栈上分配将堆分配转化为栈分配。如果一个对象在子程序中被分配要使指向该对象的指针永远不会发生逃逸对象可能是栈上分配的候选而不是堆上分配 同步省略如果一个对象被发现只有一个线程被访问到那么对于这个对象的操作可以不考虑同步。 分离对象或标量替换有的对象可能不需要作为一个连续的内存结构存在也可以被访问到那么对象的部分或全部可以不存储在内存而是存储在CPU寄存器中。 就会把这个对象拆解成若干个其中包含的若干个成员变量来代替.这个过程就是标量替换。
方法区
“尽管所有的方法区在逻辑上是属于堆的一部分但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩
方法区主要存放的是 Class而堆中主要存放的是 实例化的对象 加载大量的第三方的jar包 Tomcat部署的工程过多30~50个 大量动态的生成反射类 常量池中有什么 数量值 字符串值 类引用 字段引用 方法引用
运行时常量池Runtime Constant Pool是方法区的一部分。
常量池表Constant Pool Table是Class文件的一部分用于存放编译期生成的各种字面量与符号引用这部分内容将在类加载后存放到方法区的运行时常量池中。
运行时常量池相对于Class文件常量池的另一重要特征是具备动态性。 StringTable为什么要调整位置
stringTable回收效率不高。而我们开发中会有大量的字符串被创建回收效率低导致永久代内存不足。放到堆里能及时回收内存。
静态变量存放在那里
静态引用对应的对象实体始终都存在堆空间
方法区的垃圾收集主要回收两部分内容常量池中废弃的常量和不再使用的类型。
只要常量池中的常量没有被任何地方引用就可以被回收。
对象实例化内存布局与访问定位
对象的实例化 对象头包含了两部分分别是 运行时元数据Mark Word和 类型指针
运行时元数据 哈希值HashCode GC分代年龄 锁状态标志 线程持有的锁 偏向线程ID 偏向时间戳
类型指针
指向类元数据InstanceKlass确定该对象所属的类型。指向的其实是方法区中存放的类元信息 实例数据Instance Data
对齐填充说明
不是必须的也没有特别的含义仅仅起到占位符的作用 StringTable
注意
字符串常量池是不会存储相同内容的字符串的
String的string Pool是一个固定大小的Hashtable默认值大小长度是1009。如果放进string Pool的string非常多就会造成Hash冲突严重从而导致链表会很长而链表长了后直接会造成的影响就是当调用string.intern时性能会大幅下降。
使用-XX:StringTablesize可设置stringTab1e的长度
字符串拼接操作 常量与常量的拼接结果在常量池原理是编译期优化 常量池中不会存在相同内容的变量 只要其中有一个是变量结果就在堆中。变量拼接的原理是StringBuilder 如果拼接的结果调用intern()方法则主动将常量池中还没有的字符串对象放入池中并返回此对象地址
从上述的结果我们可以知道
如果拼接符号的前后出现了变量则相当于在堆空间中new String()具体的内容为拼接的结果
而调用intern方法则会判断字符串常量池中是否存在JavaEEhadoop值如果存在则返回常量池中的值否者就在常量池中创建
s1 s2的执行细节 StringBuilder s new StringBuilder(); s.append(a); s.append(b); s.toString(); - 类似于new String(ab);