JVM内存结构、Java内存模型(JMM)、Java对象内存模型

JVM内存结构、Java内存模型(JMM)、Java对象内存模型

1、JVM内存结构

Java虚拟机在运行时对该Java进程占用的内存进行的一种逻辑上的划分,包括方法区、堆内存(OOM)、虚拟机栈(StackOverFlowError,OOM)、本地方法栈、程序计数器。

1、局部变量表:一片连续的内存空间,用来存放方法参数,以及方法内定义的局部变量,存放着编译期间已知的数据类型(八大基本类型和对象引用(reference类型),returnAddress类型。

    Java虚拟机栈可能出现两种类型的异常:

  1. 线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError。
  2. 虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。

2、Java堆,它是所有线程共享的,它的目的是存放对象实例(所有对象实例及数组都要在堆上分配内存)。

同时它也是GC所管理的主要区域,因此常被称为GC堆,又由于现在收集器常使用分代算法

CG:

①虚拟机栈(栈桢中的本地变量表)中的引用的对象。
②方法区中的类静态属性引用的对象,一般指被static修饰引用的对象,加载类的时候就加载到内存中。
③方法区中的常量引用的对象,
④本地方法栈中JNI(native方法)引用的对象

标记清除、标记整理、复制算法

分代收集算法的原理是采用复制算法来收集新生代,采用标记/清理算法或者标记/整理算法收集老年代

 

3、方法区同堆一样,是所有线程共享的内存区域,为了区分堆,又被称为非堆。

用于存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中。

 

附:

我们知道是因为内存溢出导致的应用无法正常启动,也知道需要配置启动参数才能让应用正常启动,但是应该设置哪几个参数呢?这时就需要了解常用的参数了: 
1. -Xmx3550m:设置JVM最大堆内存为3550M。 
2. -Xms3550m:设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。 
3. -Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。 
4. -Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。 
5. -XX:NewSize=1024m:设置年轻代初始值为1024M。 
6. -XX:MaxNewSize=1024m:设置年轻代最大值为1024M。 
7. -XX:PermSize=256m:设置持久代初始值为256M。 
8. -XX:MaxPermSize=256m:设置持久代最大值为256M。 
9. -XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年轻代比年老代为1:4。 
10. -XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区(JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个Survivor区占整个年轻代大小的1/6。 
11. -XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。
参考链接:https://blog.csdn.net/u013700110/article/details/79208681

 

参考:https://www.jianshu.com/p/bf158fbb2432

https://www.jianshu.com/p/76959115d486

 

2、Java内存模型(JMM)

Java内存模型是Java语言在多线程并发情况下对于共享变量读写(实际是共享变量对应的内存操作)的规范,

主要是为了解决多线程可见性、原子性的问题,解决共享变量的多线程操作冲突问题。

JMM的两条规定
1、线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写;
2、不同的线程之间无法直接访问其他线程工作内存中的变量,线程变量值的传递需要通过主内存来完成

 

一、并发编程问题

  多线程并发编程会涉及到以下的问题:

  1)原子性:指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。

  2)可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

  3)有序性:程序执行的顺序按照代码的先后顺序执行,多线程中为了提高性能,编译器和处理器的常常会对指令做重排(编译器优化重排、指令并行重排、内存系统重排)。

 

二、解决并发编程

         1)原子性:Java提供了两个高级字节码指令monitorenter和monitorexit,对应的是关键字synchronized,使用该关键字保证方法和代码块内的操作的原子性。

         2)可见性:Java中的volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。因此,可以使用volatile来保证多线程操作时变量的可见性。

除了volatile,Java中的synchronized和final两个关键字也可以实现可见性,只不过实现方式不同

  3)有序性:用volatile关键字禁止指令重排,用synchronized关键字加锁。

 

3、Java对象模型

 java对象模型其实就是JVM中对象的内存布局。一个对象本身内在结构的描述信息以字节码的方式存储在方法区中(参见java内存区域),说白了就是class文件。那么如何获取到对象的class信息呢?虚拟机使用对象头部的一个指针指向 Class 区域,找到对象的 Class 描述。在虚拟机中,对象在内存中的存储布局分为 3 块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding):

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值