JVM调优
来源:互联网 发布:艾默生ups监控软件 编辑:程序博客网 时间:2024/06/03 22:06
最近要面试某游戏公司,听前辈们述说jvm方面考的很多,吓的我恶补JVM相关的知识
总结下原先知道的,java分栈(stack)和堆(heap),本地方法区
栈:按我的话将程序运行的时候会把main方法压入栈内,根据方法调用依次压入栈内,方法执行完成后弹栈(先进后出)
堆:存放对象和数组的地方。其中对象实例化过程
Person p=new Person()
1.JVM会读取指定路径下的Person.class文件,并加载进内存
2.在堆内存中开辟空间,分配地址。
3.并在对象空间中,对对象中的属性进行默认初始化。(基本类型初始化int为0,引用类型为null)
4.调用对应构造方法进行初始化。
5.在构造方法中,第一行会先调用父类中构造函数进行初始化。(先执行父类初始化)
6.父类初始化完毕后,再对子类的属性进行显示初始化。
7.在进行子类构造函数的特定初始化。
6.初始化完毕后,将地址赋值给引用变量。
嗯,学的太多,不总结总结就会忘记,其实我想了想,学东西多,不一定记住,但是主要是经历多了,你会在面对问题的时候心态淡然点,处事不惊。
以下是引用别人的文章!!!
虚拟机栈(Java Stack)
虚拟机栈也是线程私有的,每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈,虚拟机栈表示Java方法执行的内存模型,每调用一个方法,就会生成一个栈帧(Stack Frame)用于存储方法的本地变量表、操作栈、方法出口等信息,当这个方法执行完后,就会弹出相应的栈帧。
如果请求的栈的深度过大,虚拟机可能会抛出StackOverflowError异常,如果虚拟机的实现中允许虚拟机栈动态扩展,当内存不足以扩展栈的时候,会抛出OutOfMemoryError异常。
总结:原先在项目运行期间遇到弹出StackOverflowError的弹框,一直不在意,现在终于明白是栈太大了,分析原因可能是递归多了,遍历方法多了,或者是压栈太多,一直深入执行。而OutOfMemoryError没怎么遇见过。
栈帧(Stack Frame)
栈帧分为三部分:局部变量区(Local Variables)、操作数栈(Operand Stack)和帧数据区(Frame Data)。
总结:这个好理解,就是栈执行的时候,方法引用堆中的一个连线(指向堆)。
局部变量区(Loca Variables)
局部变量区被组织一个一个从0开始的字数组,byte、short、char在存储前被转换为int,boolean也被转换为int,0表示false,非0表示true,long和double占据两个字长。
操作数栈(Operand Stack)
操作数栈也被组织为一个字数组,但不同于局部变量区,它不是通过数组下标访问的,而是能过栈的Push和Pop操作,前一个操作Push进的数据可以被下一个操作Pop出来使用。
帧数据区(Frame Data)
这部分的作用主要有三部分:
- 常量池中数据的解析
- 方法执行完后处理方法返回,恢复调用方现场
- 方法执行过程中抛出异常时的异常处理,存储有一个异常表,当出现异常时虚拟机查找相应的异常表看是否有对应的Catch语句,如果没有就抛出异常终止这个方法调用
本地方法栈(Native Method Stack)
与虚拟机栈类似,只是是执行本地方法时使用的。JNI,就是java语言去调用C语言。很少用到,了解
方法区(Method Area)
用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译后的代码等信息。
方法区是线程间共享的,当两个线程同时需要加载一个类型时,只有一个类会请求ClassLoader加载,另一个线程会等待。
对于每一个加载的类型,会在方法区中保存以下信息:
- 类及其父类的全限定名(java.lang.Object没有父类)
- 类的类型(Class or Interface)
- 访问修饰符(public, abstract, final)
- 实现的接口的全限定名的列表
- 常量池
- 字段信息
- 方法信息
- 除常量外的静态变量
- ClassLoader引用
- Class引用
对于每一个字段,会在方法区中保存以下信息(字段声明顺序也会保存):
- 字段名
- 字段的类型
- 字段的修饰符(public, private , protected, static, final, volatile, transient)
对于每一个方法,会在方法区中保存以下信息(方法声明顺序也会保存):
- 方法名
- 方法返回类型(或void)
- 参数信息
- 方法修饰符(public, private, protected , static, final, synchronized, native, abstract)
如果方法不是抽象方法并不是本地方法(Native Method),还会保存以下信息:
- 方法的字节码
- 本地变量表及操作数栈的大小
- 异常表
虚拟机需要存储一些数据,用来快速地访问一个类对象中的方法,一般实现为一个方法表。
方法区中还有一部分是运行时常量池,主要用来存储编译时生成的字面量和符号引用,常量也可以在运行时产生,如String的intern方法。
方法区中也可能存在GC,但虚拟机规范对此不做要求,主要是回收一些常量和卸载一些不用的类型信息,不过要卸载一个类的条件很难达到,而且些处GC其实也回收不了多少内存。
总结:存放加载的类,静态变量,常量等,方法区是被线程共享的。
堆(Heap)
虚拟机中用于存放对象与数组实例的地方,垃圾回收的主要区域就是这里(还可能有方法区)。
如果垃圾收集算法采用按代收集(目前大都是这样),这部分还可以细分为新生代和老年代。
新生代又可能分为Eden区,From Survivor区和To Survivor区,主要是为了垃圾回收。所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)。
Java堆只要求逻辑上是连续的,在物理空间上可以不连续。
总结:堆是存放对象和数组实例化的地方,比栈大,并且堆是线程共享的。
直接内存
JDK1.4中引用了NIO,并引用了Channel与Buffer,可以使用Native函数库直接分配堆外内存,并通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
对象访问
当新建一个对象时,会在堆中为这个对象分配内存,并在栈中有一个对这个对象引用,除此之外,在Java堆中还要能通过这个对象找到它的类型信息(对象类型,父类,实现的接口,包含的字段与方法等)。
Reference在Java虚拟机中定义为指向对象的引用,但没有定义这个Reference应该有怎么实现。
一种实现是Reference直接存储对象在堆内的地址,对象的类型信息可以在对象在堆中的内存布局中存储,如存储在对象内存的开头等。
另一种实现是Reference指向一个句柄表中的一个位置,句柄中保存了对象的实际位置及它对应的类型信息。
使用句柄的好处是当在内存中移动对象的位置时,只需要更新句柄表中的内容,不需要改变引用值,但会多一次内存访问开销,直接引用的优缺点与此相反。
class字节码--类加载器---(方法区,堆,栈,本地方法栈),PC寄存器(每个线程都有一个寄存器),执行引擎()jvm性能优化中尽量少定义static变量。new对象,存放堆中所有线程都会共享(存在安全问题)局部变量不会反生线程安全问题是因为每个线程都有本地线程栈
java堆内存中分新生代,老年代
- JVM调优
- JVM调优
- JVM调优
- JVM调优
- JVM 调优
- JVM调优
- JVM调优
- JVM调优
- jvm调优
- JVM调优
- JVM调优
- JVM调优
- jvm调优
- JVM调优
- JVM调优
- JVM调优
- JVM调优
- JVM调优
- Linxu环境下ZooKeeper的集群搭建之详细教程
- 对于电子签名、CA、证书的理解
- vim设置配色方案
- mybatis sql语句构建器
- CSS文件引入顺序
- JVM调优
- 命令行下收集主机信息
- texteview的下划线
- git 命令
- mxnet解决AttributeError: module 'mxnet.test_utils' has no attribute 'get_mnist'这个报错
- request
- Go语言开发环境搭建与配置
- 对webapp下上传文件夹中的文件按时间排序展示到页面中
- oracle跨数据库配置