You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
这个阶段赋初始值的变量指的是那些不被final修饰的static变量,比如”public static int value = 123;”,value在准备阶段过后是0而不是123,给value赋值为123的动作将在初始化阶段才进行;比如”public static final int value = 123;”就不一样了,在准备阶段,虚拟机就会给value赋值为123。
解析
将符号引用替换为直接引用的过程,
符号引用, 包括: 类和接口的全限定名; 字段的名称和描述符; 方法的名称和描述符
例如下面这串代码:
package com.xrq.test6;
public class TestMain
{
private static int i;
private double d;
public static void print()
{
}
private boolean trueOrFalse()
{
return false;
}
}
public class SuperClass
{
public static int value = 123;
static
{
System.out.println("SuperClass init");
}
}
public class SubClass extends SuperClass
{
static
{
System.out.println("SubClass init");
}
}
public class TestMain
{
public static void main(String[] args)
{
System.out.println(SubClass.value);
}
}
运行结果为
SuperClass init
2、通过数组定义引用类,不会触发此类的初始化
public class SuperClass
{
public static int value = 123;
static
{
System.out.println("SuperClass init");
}
}
public class TestMain
{
public static void main(String[] args)
{
SuperClass[] scs = new SuperClass[10];
}
}
3、引用静态常量时,常量在编译阶段会存入类的常量池中,本质上并没有直接引用到定义常量的类
public class ConstClass
{
public static final String HELLOWORLD = "Hello World";
static
{
System.out.println("ConstCLass init");
}
}
public class TestMain
{
public static void main(String[] args)
{
System.out.println(ConstClass.HELLOWORLD);
}
}
运行结果为
Hello World
https://dzone.com/articles/how-use-verbose-options-java
-verbose:class is used to display the information about classes being loaded by JVM. This is useful when using class loaders for loading classes dynamically or for analysing what all classes are getting loaded in a particular scenario.
问题
spring是如何运行起来的, 并维持程序一直运行, 不结束
The text was updated successfully, but these errors were encountered:
运行时数据区域
程序计数器(programme counter register)
若执行的是非native方法, 则保存下条指令的地址; 若是native方法, 则为空;每个线程独有, 互不影响
虚拟机栈(virtual machine stacks), 本地方法栈(native method stack)
每个线程独有, 每个方法创建的时候都会创建一个栈帧(stack frame),用于存储方法的局部变量, 操作数栈等.
, 虚拟机栈和本地方法栈的不同是,前者执行java方法, 后者执行native方法
线程请求的栈深度大于虚拟机允许的栈深度, 抛出该异常
如果虚拟机栈可以动态扩展,但是扩展时无法申请到固定的内存,会抛出OutOfMemoryError
java 堆(heap)
存放对象的实例和数组, 所有线程所共有; 如果堆中没有内存完成实例的分配, 并且堆也无法再扩展时,抛出 OutOfMemoryError
方法区(Method Area)
线程间共享, 存储每个类的结构,包括运行时常量 (包括string pool) ,静态变量,即时编译器编译后的代码等数据
本地内存(native memory, C heap)
DirectBuffer访问更快,避免了数据从heap memory 拷贝到本地堆。DirectBuffer byte array 实际是保存在native heap中,但是操作该byte array的对象保存在java heap中。
GC时不会直接回收native memory, 通过释放heap memory中的对象来释放native memory, 但是通常java heap没达到gc 的条件.
虚拟机对象
创建
每个线程分配一块独立的内存,本地线程分配缓冲(Thread local allocation buffer),来控制给每个对象分配内存时是线程安全的
对象的内存布局
对象头, 实例数据, 对齐填充
对象的访问
sun hotspot通过直接指针的方式, reference存储了对象的地址,存储在栈区(应该指的是虚拟机栈),直接访问到堆中的对象的数据,对象的数据中包含类的信息.
内存泄漏原因
java heap分析工具
问题
三. 垃圾回收
可达性分析
主要针对类的回收,满足以下三个条件:
引用
只要强引用存在, 则永远不会被回收
软引用
SoftReference来实现, 除非内存准备溢出了, 不然不会被回收.
弱引用
WeakReference的对象, 若只被弱引用引用, 不被其他任何强引用引用时, 如果GC运行, 那么该对象就会被回收.例子 ThreadLocal中的Entry 持有对ThreadLocal对象的弱引用, 若所有使用该ThreadLocal的线程均退出,
虚引用
垃圾收集策略
需要回收的对象进行一次标记,标记完成后统一回收
缺点
标记-复制算法
将内存分配为一块eden和两块survivor, 比例是8:1, 每次使用新生代内存的90%, gc时将存活对象复制到空闲的survivor, 剩余对象一次性清理
缺点
标记整理算法
将可用的对象向一端移动,然后清理掉边界以外的内存,有效避免了内存碎片(针对标记清除算法)和需要有一部分空间来作为留存空间(针对标记-复制算法).
分代收集算法
对新生代和老年代采用不同的垃圾收集算法, 例如HotSpot新生代采用标记-复制, 老年代采用标记整理
垃圾收集器
新生代收集器, 注重控制吞吐量来控制GC的停顿时间, 虚拟机运行100分钟, GC一分钟, 吞吐量 99%
重要参数 : -XX:MaxGCPauseMills , -XX:GCTimeRatio, -XX:UseAdaptiveSizePolicy(自适应调节) 与ParNew的最大区别
是Serial 的老年代版本, 与Parallel Scavenage配合使用, 作为CMS的备案, 在发生concurrent mode failure使用
是Parallel Scavenage的老年代版本, 可与Parallel Scavenage 配合使用
主要是将回收时间降至最短,基于标记-清除算法,
缺点
Garbage First(G1)
整体使用标记-整理, 局部采用标记-复制,故不会有内存碎片.
问题
java heap 分代(基于jdk1.8)
GC回收过程
当对象大小大于参数设置的 -XX:PretenureSizeThreshold (默认是 0 , 即无论多大不会直接进入老年代)
当对象出生在eden, 经过一次minorGC进入survivor, 则对象年龄设为1, 当对象年龄超过该参数 -XX:MaxTenuringThreshold 默认15, 则进入老年代
当某个年龄的对象的大小总和超过survivor的一半, 则大于或等于该年龄的所有对象都会进入老年代.
minorGC之前会检查老年代最大的连续内存空间是否大于新生代所有对象的大小, 或者检查最大的老年代的连续内存是否大于历史平均进入老年代的对象大小, 满足一个就进行minorGC, 否则fullGC
GC调优案例
hotSpot的算法实现
当eden和一个survivor的空间容不下新的对象时,产生minorGC,将长期存活对象移到老年代, 若老年代的空间不够, 则进行fullGC
寻找GC Root的引用链
但是导致引用变化的指令可能非常多, 可能导致 OOP Map的所占空间巨大
开发中的GC优化
类加载机制
参考自 link
例如下面这串代码:
用javap把这段代码的.class反编译一下:
1、子类引用父类静态字段,不会导致子类初始化。至于子类是否被加载、验证了,前者可以通过”-XX:+TraceClassLoading”来查看
2、通过数组定义引用类,不会触发此类的初始化
3、引用静态常量时,常量在编译阶段会存入类的常量池中,本质上并没有直接引用到定义常量的类
在编译阶段通过常量传播优化,常量HELLOWORLD的值”Hello World”实际上已经存储到了NotInitialization类的常量池中,以后NotInitialization对常量ConstClass.HELLOWORLD的引用实际上都被转化为NotInitialization类对自身常量池的引用了。也就是说,实际上的NotInitialization的Class文件中并没有ConstClass类的符号引用入口,这两个类在编译成Class之后就不存在任何联系了。
类与类的加载器
只要当两个类来自同一个class文件,被同一个虚拟机加载,类加载器相同, equals(), isAssignableFrom(), instanceof 才能返回两个类相等.
双亲委派模型(parents delegation model)
当一个类加载器收到了类加载的请求, 首先把请求委派给父类加载器执行, 所以所以的加载请求都会首先传递到顶层的启动类加载器, 当父类无法加载时,子加载器才会尝试自己加载
Bootstrap ClassLoader -> Extension ClassLoader -> Application ClassLoader -> User ClassLoader
classpath
可以参考honghailiang888, 非常齐全
如何手动编译并运行 java文件
class文件发现规则:class文件所在目录 = classpath + '' + 包名中的'.'全变成'', 一般会把运行java, javac程序的当前目录(.)也加入到classpath中, 然后会遍历所有的classpath, 在每个classpath下面找 包名+类名 对应的class文件.
当需要在任意目录编译 A.java时, 需要知道所引用的B.java的位置, 假设运行javac的目录为D: , 因为当前目录是D:, 在当前目录下用包名无法找到B.java, 故需要手动指定额外的classpath, 则会在packageA和packageB生成各自的class文件.
也需要通过classpath 找到对应的class文件, 并且需要指定 包名.类名 , 项目结构如之前所示, 在D: 下运行java,
jvm常用命令
问题
The text was updated successfully, but these errors were encountered: