一、方法区存放内容:
1.类的全限定名(类的全路径名)。
2.类的直接超类的权全限定名(如果这个类是Object,则它没有超类)。
3.类的类型(类或接口)。
4.类的访问修饰符,public,abstract,final等。
5.类的直接接口全限定名的有序列表。
6.常量池(字段,方法信息,静态变量,类型引用(class))等
当jvm使用类装载器装在某个类时,它首先要定位到对应的class文件,然后读入这个class文件,最后提取该文件的内容信息,并将这些信息存储到方法区,最后返回一个class实例。
方法区是系统分配的一个内存逻辑区域,是一块所有线程共享的内存区域,用来存储类型信息(类型信息可以理解为类的描述信息(类的全限定名,访问修饰符,字段,方法等)),方法区的大小决定了系统可以包含多少个类,如果系统类太多,方法区内存不够会导致方法区溢出,虚拟机同样会抛出内存溢出信息。方法去特点:
1.方法区是线程安全的,由于所有的线程都共享方法区,所以方法区里的数据访问必须被设计成线程安全的。例如,假如同时有两个线程都企图访问方法区中的同一个类,而这个类还没有被装入jvm,那么只允许一个线程去装在它,而其他线程必须等待。
2.方法去的大小不必是固定的,jvm可根据应用需要动态调整,同时,方法区也不一定是连续的,方法区可以在一个堆(甚至是jvm自己的堆)中自由分配。
3.方法区也可被垃圾收集,当某个类不在被使用时,jvm将卸载这个类,进行垃圾收集。
String str=new String("hello");
堆 存放的是new出来的对象 jvm中只有一个堆区 被所有的线程共享
栈 是变量str 每个栈中的数据私有的 其他栈不能访问。
方法区 是“hello”;被所有的线程共享,方法区包含所有的class static变量。
程序计数器:
一、先来看看概念
多线程的Java应用程序:为了让每个线程正常工作就提出了程序计数器(Programe Counter Register),每个线程都有自己的程序计数器这样当线程执行切换的时候就可以在上次执行的基础上继续执行,仅仅从一条线程线性执行的角度而言,代码是一条一条的往下执行的,这个时候就是程序计数器;JVM就是通过读取程序计数器的值来决定下一条需要执行的字节码指令,进而进行选择语句、循环、异常处理等;
这个还没看懂的话不要紧,继续往下走咯。
二、简单粗暴的举例
1.生活中的案例
比如老王正在看电影,他看到三十五分钟的时候,突然他的QQ好友给他开视频聊天,这时候肯定打断他看电影了,假设他qq好友和他视频完了,他肯定要接着他那35分钟的进度去继续看,这时候他怎么知道我看到35分钟了?这时候程序计数器就起了作用,他负责管理进度。
是不是略微懂了一点呢?
2.代码层面的案例
将上面的例子转换成代码,是这样的:
A线程正在执行HelloWorld.class的第三十五行。这时候CPU时间片被B线程抢走了,当A线程重新被分配到时间片时,他怎么知道我的class运行到哪了?这时候他可以看程序计数器在哪个位置。
这下总该明白了吧?
三、JVM程序计数器的总结
程序计数器作用不多说了,我个人感觉他是为了多线程而生的,单线程情况下完全不需要他。从案例中不难发现,程序计数器是每个线程独有的,并非线程共享的,所以是线程安全的!
本地方法栈:
对于一个运行中的Java程序而言,它还可能会用到一些跟本地方法相关的数据区。当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。本地方法可以通过本地方法接口来访问虚拟机的运行时数据区,但不止如此,它还可以做任何它想做的事情。
本地方法本质上时依赖于实现的,虚拟机实现的设计者们可以自由地决定使用怎样的机制来让Java程序调用本地方法。
任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。
如果某个虚拟机实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。当C程序调用一个C函数时,其栈操作都是确定的。传递给该函数的参数以某个确定的顺序压入栈,它的返回值也以确定的方式传回调用者。同样,这就是虚拟机实现中本地方法栈的行为。
很可能本地方法接口需要回调Java虚拟机中的Java方法,在这种情况下,该线程会保存本地方法栈的状态并进入到另一个Java栈。
下图描绘了这样一个情景,就是当一个线程调用一个本地方法时,本地方法又回调虚拟机中的另一个Java方法。
这幅图展示了JAVA虚拟机内部线程运行的全景图。一个线程可能在整个生命周期中都执行Java方法,操作它的Java栈;或者它可能毫无障碍地在Java栈和本地方法栈之间跳转。
该线程首先调用了两个Java方法,而第二个Java方法又调用了一个本地方法,这样导致虚拟机使用了一个本地方法栈。假设这是一个C语言栈,其间有两个C函数,第一个C函数被第二个Java方法当做本地方法调用,而这个C函数又调用了第二个C函数。之后第二个C函数又通过本地方法接口回调了一个Java方法(第三个Java方法),最终这个Java方法又调用了一个Java方法(它成为图中的当前方法)。