MYSQL进阶篇一篇文章带你深入掌

北京哪个医院治疗白癜风手术好 http://baidianfeng.39.net/a_zhiliao/150911/4694665.html

一、引言

1.什么是JVM?

定义:JavaVirtualMachine-java程序的运行环境(java二进制字节码的运行环境)

好处:

一次编写,到处运行

自动内存管理,垃圾回收功能

数组下标越界检查

多态

比较jvm、jre、jdk

2.学习JVM有什么用

理解底层的实现原理

中高级程序员的必备技能

3.常见的JVM

4.学习路线

二、内存结构

1.程序计数器

1.1定义

ProgramCounterRegister程序计数器(寄存器)在物理上:位于寄存器作用:是记住下一条jvm指令的执行地址特点:

是线程私有的

不会存在内存溢出

1.2作用

0:getstatic#20//PrintStreamout=System.out;3:astore_1//--4:aload_1//out.println(1);5:iconst_1//--6:invokevirtual#26//--9:aload_1//out.println(2);10:iconst_2//--11:invokevirtual#26//--14:aload_1//out.println(3);15:iconst_3//--16:invokevirtual#26//--19:aload_1//out.println(4);20:iconst_4//--21:invokevirtual#26//--24:aload_1//out.println(5);25:iconst_5//--26:invokevirtual#26//--29:return

解释器会解释指令为机器码交给cpu执行,程序计数器会记录下一条指令的地址行号,这样下一次解释器会从程序计数器拿到指令然后进行解释执行。

多线程的环境下,如果两个线程发生了上下文切换,那么程序计数器会记录线程下一行指令的地址行号,以便于接着往下执行。

2.虚拟机栈

2.1定义

JavaVirtualMachineStacks(Java虚拟机栈)

每个线程运行时所需要的内存,称为虚拟机栈

每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存

每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

问题辨析

垃圾回收是否涉及栈内存?栈内存并不涉及垃圾回收,栈内存的产生就是方法一次一次调用产生的栈帧内存,而栈帧内存在每次方法被调用后都会被弹出栈,自动就被回收掉,不需要垃圾回收。来管理

栈内存分配越大越好吗?不是,在线程不多的情况下,栈内存分配大在递归时能提高运行速度,但他会影响线程的数目,从而影响到整个系统的运行速度

方法内的局部变量是否线程安全?如果方法内局部变量没有逃离方法的作用访问,它是线程安全的如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全,

如果是共享的需要考虑线程安全,如果是私有的不用考虑线程安全

2.2栈内存溢出

栈帧过多导致栈内存溢出----一般递归的时候容易出现

栈帧内存过大导致栈内存溢出---不易出现

StackOverflowError栈内存溢出异常

JsonIgnore

2.3线程运行诊断

案例1:cpu占用过多定位

用top定位哪个进程对cpu的占用过高

psH-eopid,tid,%cpu

grep进程id(用ps命令进一步定位是哪个线程引起的cpu占用过高)

jstack进程id可以根据线程id找到有问题的线程,进一步定位到问题代码的源码行号

3.本地方法栈

一些带有native关键字的方法就是需要JAVA去调用本地的C或者C++方法,因为JAVA有时候没法直接和操作系统底层交互,所以需要用到本地方法栈,服务于带native关键字的方法。

为本地的方法提供一个运行的空间

4.堆

4.1定义

Heap堆

通过new关键字,创建对象都会使用堆内存特点

它是线程共享的,堆中对象都需要考虑线程安全的问题

有垃圾回收机制

4.2堆内存溢出

jps工具查看当前系统中有哪些java进程

jmap工具查看堆内存占用情况jmap-heap进程id

jconsole工具图形界面的,多功能的监测工具,可以连续监测

jvisualvm工具

5.方法区

5.1方法区

Java虚拟机有一个在所有Java虚拟机线程之间共享的方法区。方法区类似于传统语言的编译代码的存储区,或者类似于操作系统进程中的“文本”段。它存储每个类的结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括类和实例初始化以及接口初始化中使用的特殊方法。

方法区是在虚拟机启动时创建的。尽管方法区在逻辑上是堆的一部分,但简单的实现可能会选择不进行垃圾收集或压缩它。本规范不要求方法区域的位置或用于管理已编译代码的策略。方法区域可以是固定大小,也可以根据计算需要扩大,如果不需要更大的方法区域,可以缩小。方法区的内存不需要是连续的。

Java虚拟机实现可以为程序员或用户提供对方法区域初始大小的控制,以及在方法区域大小可变的情况下,对最大和最小方法区域大小的控制。

以下异常情况与方法区相关:

如果方法区域中的内存无法满足分配请求,Java虚拟机将抛出一个OutOfMemoryError.JVM规范-方法区定义

5.2组成

5.3方法区内存溢出

1.8以前会导致永久代内存溢出演示永久代内存溢出java.lang.OutOfMemoryError:PermGenspace-XX:MaxPermSize=8m

1.8之后会导致元空间内存溢出演示元空间内存溢出java.lang.OutOfMemoryError:Metaspace-XX:MaxMetaspaceSize=8m场景:

springmybatis

5.4运行时常量池

//二进制字节码(类基本信息,常量池,类方法定义,包含了虚拟机指令)publicclassTest{publicstaticvoidmain(String[]args){System.out.println("helloworld");}}

然后使用javap-vTest.class命令反编译查看结果:每条指令都会对应常量池表中一个地址,常量池表中的地址可能对应着一个类名、方法名、参数类型等信息。

常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息

运行时常量池,常量池是*.class文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

5.5StringTable

面试题:

Strings1="a";Strings2="b";Strings3="a"+"b";//abStrings4=s1+s2;//newString("ab")Strings5="ab";Strings6=s4.intern();//问System.out.println(s3==s4);//falseSystem.out.println(s3==s5);//trueSystem.out.println(s3==s6);//trueStringx2=newString("c")+newString("d");//newString("cd")x2.intern();Stringx1="cd";//问,如果调换了的位置呢,如果是jdk1.6呢System.out.println(x1==x2);//jdk1.6://Stringx1="cd";x2.intern();//x2.intern();falseStringx1="cd";ture//jdk1.8://Stringx1="cd";x2.intern();//x2.intern();falseStringx1="cd";ture

练习:

//StringTable["a","b","ab"]hashtable结构,不能扩容publicclassDemo1_22{//常量池中的信息,都会被加载到运行时常量池中,这时abab都是常量池中的符号,还没有变为java字符串对象//ldc#2会把a符号变为"a"字符串对象//ldc#3会把b符号变为"b"字符串对象//ldc#4会把ab符号变为"ab"字符串对象publicstaticvoidmain(String[]args){Strings1="a";//懒惰的Strings2="b";Strings3="ab";Strings4=s1+s2;//newStringBuilder().append("a").append("b").toString()newString("ab")Strings5="a"+"b";//javac在编译期间的优化,结果已经在编译期确定为abSystem.out.println(s3==s4);//s3是在串池中的,而s4则是在堆中,所有不相等System.out.println(s3==s5);//true}}

使用javap-vDemo1_22.class命令

5.6StringTable的特性

常量池中的字符串仅是符号,第一次用到时才变为对象

利用串池的机制,来避免重复创建字符串对象

字符串变量拼接的原理是StringBuilder(1.8)

字符串常量拼接的原理是编译期优化

可以使用intern方法,主动将串池中还没有的字符串对象放入串池1.8将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,会把串池中的对象的引用返回1.6将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池,会把串池中的对象返回

5.7StringTable位置

jdk1.6StringTable位置是在永久代中,1.8StringTable位置是在堆中。

5.8StringTable垃圾回收

-Xmx10m指定堆内存大小-XX:+PrintStringTableStatistics打印字符串常量池信息-XX:+PrintGCDetails-verbose:gc打印gc的次数,耗费时间等信息

演示StingTable垃圾回收:

publicstaticvoidmain(String[]args)throwsInterruptedException{inti=0;try{for(intj=0;j;j++){//j=,j=00String.valueOf(j).intern();i++;}}catch(Throwablee){e.printStackTrace();}finally{System.out.println(i);}}

5.9StringTable性能调优

调整-XX:StringTableSize=桶个数

考虑将字符串对象是否入池

6.直接内存

6.1定义

DirectMemory

常见于NIO操作时,用于数据缓冲区

分配回收成本较高,但读写性能高

不受JVM内存回收管理

文件读写过程(IO):

因为java不能直接操作文件管理,需要切换到内核态,使用本地方法进行操作,然后读取磁盘文件,会在系统内存中创建一个缓冲区,将数据读到系统缓冲区,然后在将系统缓冲区数据,复制到java堆内存中。缺点是数据存储了两份,在系统内存中有一份,java堆中有一份,造成了不必要的复制。

使用了DirectBuffer文件读取流程:直接内存是操作系统和Java代码都可以访问的一块区域,无需将代码从系统内存复制到Java堆内存,从而提高了效率。

6.2分配和回收原理

使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法

ByteBuer的实现类内部,使用了Cleaner(虚引用)来监测ByteBuer对象,一但ByteBuer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory方法来释放直接内存。

来源:


转载请注明:http://www.aierlanlan.com/rzfs/1496.html