认识java虚拟机

java概念图描述:

1.内存溢出模拟:

往堆里无限存储对象,直到堆内存溢出

新建Demo类 

public class Demo {

}

创建Main:

public class Main {

	public static void main(String[] args) {
		List<Demo> demoList=new ArrayList<>();
		while(true) {
			demoList.add(new Demo());
		}

	}

}

运行程序会发现:当运行内存达到6.3G时会报java堆空间问题:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

使用eclipse的工具:配置vm参数:堆存储快照

-XX:+HeapDumpOnOutOfMemoryError

指定vm的运行大小为20M

-XX:+HeapDumpOnOutOfMemoryError -Xms20m -Xmx20m

将堆存储成快照,并保存下来了:

Dumping heap to java_pid4820.hprof ...

可以通过eclipse的Memory Analyzer插件打开快照

(打开支配树,shallow heap对象本身所占内存,不包括引用对象,retained heap当前对象+引用对象,二者总的内存,垃圾回收就能释放这样多的内存)

进一步分析发现是由于demo类多次创建从而导致内存溢出:

java监视和管理控制台

jdk/bin/jConsole.exe实际依赖于lib/tools.jar包

内存池是垃圾回收器最喜欢光顾的地方

运行方式:

code(java语言编写的代码)-----》编译器------》class(字节码文件)-------》jvm(java虚拟机)

lamda表达式和函数式编程

类似    x -> y

x是函数的参数,y是关键的执行语句

 jb.addActionListener(new ActionListener() {
        	 @Override
        	 public void actionPerformed(ActionEvent e) {
        		 System.out.println("click");
        	 }
         })
         
         //简写成
         //jb.addActionListener(event -> System.out.println("click"));

 java内存管理:

 1.程序计数器:

类似于图中的红色箭头:指向当前正在执行的代码行:

1.不过图中的123456的行号,如果是java方法,在计数器中记录的正在执行的虚拟机字节码指令的地址。 是native方法,计数器的值为undefined

2.此区域是唯一一个在java虚拟机规范中没有规定任何outOfMemoryError情况的区域

java虚拟机栈:(为执行java方法服务)

栈帧:

用于存储局部变量表,操作数栈,动态链接,方法出口等

每个方法都有栈帧,调用方法,栈帧进站,方法执行完后,栈帧就出站,然后被销毁、

(栈帧就是用来动态描述方法执行过程的)

局部变量表:

存放编译期可知的基本数据类型,引用类型,returnAddress类型。。(里面也存着对象的引用【对象句柄】)

局部变量表的内存空间在编译器完成分配,当进入一个方法时,这个方法需要在帧分配多少内存是固定的,在方法运行期间是不会改变局部变量表的大小

public class Stack {
	private void tes() {
		System.out.println("方法正在执行");
		tes();
	}
         public static void main(String[] args) {
        	 new Stack().tes();
         }
}

 会报StackOverFlowError或者OutOfMemory

本地方法栈(为虚拟机执行native方法服务的)

java堆:

1.1    存放对象实例的(内存管理最大的一块,对象都会放在这里)

1.2 垃圾收集器管理的主要区域

1.3  新生代,老年代 ,Eden空间

方法区

存储虚拟机加载的类信息【类的版本,字段,方法,接口】,常量,静态变量,即时编译器编译后的代码等数据

运行时常量池:

java中虽然没有指针,但引用就是引用了它的地址(开发者而言是隐式的,看不到),引用就相当于指针,它是对象的内存地址

字符串的创建会放到常量池里面(常量池是方法区的一部分)

s1的实例abc和s2的实例abc都会被丢到hashSet里面,有由于hashset是集合,无重复元素,故只有一个实例abc会被创建,所以s1和s2引用的都是同一个实例,因此s1==s2,new关键字创建的实例一定是在堆中的,字符串的实例是放在运行时常量池中的

public class Main {
	public static void main(String[] args) {
	 String s1="abc";
	 String s2="abc";
	 String s3=new String("abc");
	 System.out.println(s1==s2);   //true
	 System.out.println(s1==s3);   //false
	 //intern()将堆中的常量移到运行时常量池中,它是原生本地的方法实现的
     //public native String intern();
	 System.out.println(s1==s3.intern());  //true
	}	
	
}

直接内存(堆之外的内存,,比如new Input/Output 即NIO) 

猜你喜欢

转载自blog.csdn.net/qq_41063141/article/details/85831566
今日推荐