第一篇 初识Java虚拟机 - Go语言中文社区

第一篇 初识Java虚拟机


 

 

   一. Java虚拟机的基本结构以及各个部分的作用

          

 



       
每个区域各司其职


  • 1.     类加载系统负责从文件系统或者网络中加载class信息       
  • 2.     方法区存放加载的class信息
  • 3.     java堆存放几乎所有的对象实例。所有线程共享.
  • 4.     直接内存可以用java nio库来分配,直接内存访问速度优于堆内存,但是分配速度会慢,如果频繁读写,那么可以使用它.
  • 5.     Java栈是线程独用的,线程创建时会分配.里面保存着局部变量,方法参数。
  • 6.     本地方法栈用于本地方法调用
  • 7.     PC寄存器是每个线程的私有空间.如果一个线程正在执行的方法不是本地方法,它会指向当前正在被执行的指令.否则,它的值就是undefined.

 

     二.java 堆结构

 

       根据垃圾回收机制的不同,Java堆有不同的结构.最为常见的是java分为新生代和老年代.新生代又分eden,s0,s1,s0,s1也被称为fromto区域,他们大小相等.



 

              一个对象的分配过程可以这样来描述

 

  •        1.         new一个对象的时候会把它分配在eden,
  •        2.        进行一次回收后,如果这个对象还存在,会进入s0或者s1
  •        3.        之后没进行一次垃圾回收,它的年龄就会+1
  •        4.        当它达到一定年龄时,就会进入老年代

 

 

 

       三.函数如何调用

 

         java栈是先进后出的数据结构,主要保存的数据结构是栈帧。

         举例:假设函数1调用函数2函数2调用函数3,那么当函数1被调用时,入栈,函数2被调用时,入栈。以此类推, 一直执行,当前执行的函数对应的帧保存着当前函数对应      的局部变量,中间运算陈果.

          Java又两种返回函数方式,return和抛异常.

         一个栈帧中至少要包含局部变量表,操作数栈和帧数据.-Xss用来指定最大栈空间,它决定了函数调用最大深度.

         局部变量表中的参数个数也决定了函数最大调用深度.用不断递归调用自身的方法可以使得栈空间溢出.

          

         局部变量表
        
      
         里面包含了索引(index ),名字(name),数据类型(descriptor),里面的每一行代表一个槽位,如果一个局部变量过了作用域,那么再它之后申明的变量会复用这个槽位.

             举例说明加上这个参数查看gc情况.  -XX:+PrintGC


public  void local1(){
		//数组被a引用,不会回收
		byte[] a  =new byte [1024*1024];
		System.gc();}
	public  void local2(){
		//byte失去引用,顺利回收
		byte[] a  =new byte [1024*1024];
		a=null;
		System.gc();
	}
	public  void local3(){
		//虽然离开作用域,a仍然存在局部表量表
		{
		byte[] a  =new byte [1024*1024];
		}
		System.gc();
	}
	public  void local4(){
		//a的槽位被c占用
		{
		byte[] a  =new byte [1024*1024];
		}
		int c= 1;
		System.gc();
	}
	public  void local5(){
		//方法执行结束,栈空间弹出,栈帧被销毁
		local1();
		System.gc();
	}
	public static void main(String[] args) {
		LocalVarTableTest l = new LocalVarTableTest();
		l.local1();
	}

 

       栈上分配

       栈上分配是一项优化技术.对于那些不会被其他线程访问到的对象,直接打散分配在栈上.函数调用结束后自行销毁,不需要垃圾回收.

       它的一个技术基础是进行逃逸分析,判断对象的作用域是否能逃逸出函数体,例如,静态成员变量,以及return 返回.

      例子:jvm参数-server  -Xms10m -Xmx10m -XX:+PrintGC -XX:+DoEscapeAnalysis -XX:-UseTLAB -XX:+EliminateAllocations

      可以去掉-XX:+DoEscapeAnalysis -XX:-UseTLAB -XX:+EliminateAllocations

     

public class StackRunawayTest {
	private static class User{
		public int id =0;
		public String name="";
	}

	public static void alloc(){
		User u = new User();//对象头暂居8个字节
		u.id =1 ;//4个字节
		u.name="ffff";//4个字节
		//总共16个字节.
	}
	public static void main(String[] args) {
		for(int i = 0;i<3;i++){
			//创建1亿个对象,约等于1.5g,如果堆空间小于这个值可定会抛出异常
			alloc();
		}
	}
}

 试一试两种情况执行的效率以及GC的情况,你就会发现使用逃逸分析的效率明显高于不使用逃逸分析.

       方法区

         方法区在jdk1.61.7可以理解为永久区(Perm).默认是64M.用参数

     -XX:PermSize -XX:MaxPermSize设置.如果系统使用了动态代理,那么就会出现大量的类把空间撑爆。

     在jdk1.8,取而代之的是元数据区.用参数-XX:MaxMetaspaceSize指定.这个属于堆外直接内存,如果不指定大小,会耗尽所有内存.

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/zcf9916/article/details/84914950
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-09-13 23:01:12
  • 阅读 ( 1552 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢