当前位置: 首页 > news >正文

建设网站所有步骤外包服务公司排名

建设网站所有步骤,外包服务公司排名,做论坛网站 备案,网络销售怎么做才能做好Java 内存区域和内存模型是不一样的东西#xff0c;内存区域是指 Jvm 运行时将数据分区域存储#xff0c;强调对内存空间的划分。 而内存模型#xff08;Java Memory Model#xff0c;简称 JMM #xff09;是定义了线程和主内存之间的抽象关系#xff0c;即 JMM 定义了 …Java 内存区域和内存模型是不一样的东西内存区域是指 Jvm 运行时将数据分区域存储强调对内存空间的划分。 而内存模型Java Memory Model简称 JMM 是定义了线程和主内存之间的抽象关系即 JMM 定义了 JVM 在计算机内存(RAM)中的工作方式如果我们要想深入了解Java并发编程就要先理解好Java内存模型。 Java运行时数据区域 众所周知Java 虚拟机有自动内存管理机制如果出现内存泄漏和溢出方面的问题排查错误就必须要了解虚拟机是怎样使用内存的。 下图是 JDK8 之后的 JVM 内存布局。 JDK8 之前的内存区域图如下: 在 HotSpot JVM 中永久代中用于存放类和方法的元数据以及常量池比如Class和Method。每当一个类初次被加载的时候它的元数据都会放到永久代中。 永久代是有大小限制的因此如果加载的类太多很有可能导致永久代内存溢出即万恶的 java.lang.OutOfMemoryError: PermGen 为此我们不得不对虚拟机做调优。 那么Java 8 中 PermGen 为什么被移出 HotSpot JVM 了我总结了两个主要原因: 由于 PermGen 内存经常会溢出引发恼人的 java.lang.OutOfMemoryError: PermGen因此 JVM 的开发者希望这一块内存可以更灵活地被管理不要再经常出现这样的 OOM移除 PermGen 可以促进 HotSpot JVM 与 JRockit VM 的融合因为 JRockit 没有永久代。 根据上面的各种原因PermGen 最终被移除方法区移至 Metaspace字符串常量移至 Java Heap。 程序计数器 程序计数器Program Counter Register是一块较小的内存空间它可以看作是当前线程所执行的字节码的行号指示器。 由于 Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的在任何一个确定的时刻一个处理器内核都只会执行一条线程中的指令。 因此为了线程切换后能恢复到正确的执行位置每条线程都需要有一个独立的程序计数器各条线程之间计数器互不影响独立存储我们称这类内存区域为“线程私有”的内存。 如果线程正在执行的是一个 Java 方法这个计数器记录的是正在执行的虚拟机字节码指令的地址如果正在执行的是 Native 方法这个计数器值则为空Undefined。此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。 Java虚拟机栈 与程序计数器一样Java 虚拟机栈Java Virtual Machine Stacks也是线程私有的它的生命周期与线程相同。 虚拟机栈描述的是 Java 方法执行的内存模型每个方法在执行的同时都会创建一个栈帧Stack Frame是方法运行时的基础数据结构用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。 在活动线程中只有位于栈顶的帧才是有效的称为当前栈帧。正在执行的方法称为当前方法栈帧是方法运行的基本结构。在执行引擎运行时所有指令都只能针对当前栈帧进行操作。 1. 局部变量表 局部变量表是存放方法参数和局部变量的区域。 局部变量没有准备阶段 必须显式初始化。如果是非静态方法则在 index[0] 位置上存储的是方法所属对象的实例引用一个引用变量占 4 个字节随后存储的是参数和局部变量。字节码指令中的 STORE 指令就是将操作栈中计算完成的局部变呈写回局部变量表的存储空间内。 虚拟机栈规定了两种异常状况如果线程请求的栈深度大于虚拟机所允许的深度将抛出 StackOverflowError 异常如果虚拟机栈可以动态扩展当前大部分的 Java 虚拟机都可动态扩展如果扩展时无法申请到足够的内存就会抛出 OutOfMemoryError 异常。 2. 操作栈 操作栈是个初始状态为空的桶式结构栈。在方法执行过程中 会有各种指令往 栈中写入和提取信息。JVM 的执行引擎是基于栈的执行引擎 其中的栈指的就是操 作栈。字节码指令集的定义都是基于栈类型的栈的深度在方法元信息的 stack 属性中。 i 和 i 的区别 i从局部变量表取出 i 并压入操作栈(load memory)然后对局部变量表中的 i 自增 1(addstore memory)将操作栈栈顶值取出使用如此线程从操作栈读到的是自增之前的值。i先对局部变量表的 i 自增 1(load memoryaddstore memory)然后取出并压入操作栈(load memory)再将操作栈栈顶值取出使用线程从操作栈读到的是自增之后的值。 之前之所以说 i 不是原子操作即使使用 volatile 修饰也不是线程安全就是因为可能 i 被从局部变量表内存取出压入操作栈寄存器局部变量表内存中自增使用栈顶值更新局部变量表寄存器更新写入内存其中分为 3 步volatile 保证可见性保证每次从局部变量表读取的都是最新的值但可能这 3 步可能被另一个线程的 3 步打断产生数据互相覆盖问题从而导致 i 的值比预期的小。 3. 动态链接 每个栈帧中包含一个在常量池中对当前方法的引用 目的是支持方法调用过程的动态连接。 4.方法返回地址 方法执行时有两种退出情况 正常退出即正常执行到任何方法的返回字节码指令如 RETURN、IRETURN、ARETURN 等异常退出。 无论何种退出情况都将返回至方法当前被调用的位置。方法退出的过程相当于弹出当前栈帧退出可能有三种方式 返回值压入上层调用栈帧。异常信息抛给能够处理的栈帧。PC计数器指向方法调用后的下一条指令。 本地方法栈 本地方法栈Native Method Stack与虚拟机栈所发挥的作用是非常相似的它们之间的区别不过是虚拟机栈为虚拟机执行 Java 方法也就是字节码服务而本地方法栈则为虚拟机使用到的 Native 方法服务。Sun HotSpot 虚拟机直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样本地方法栈区域也会抛出 StackOverflowError 和 OutOfMemoryError 异常。 线程开始调用本地方法时会进入 个不再受 JVM 约束的世界。本地方法可以通过 JNI(Java Native Interface)来访问虚拟机运行时的数据区甚至可以调用寄存器具有和 JVM 相同的能力和权限。 当大量本地方法出现时势必会削弱 JVM 对系统的控制力因为它的出错信息都比较黑盒。对内存不足的情况本地方法栈还是会抛出 nativeheapOutOfMemory。 JNI 类本地方法最著名的应该是 System.currentTimeMillis() JNI使 Java 深度使用操作系统的特性功能复用非 Java 代码。 但是在项目过程中 如果大量使用其他语言来实现 JNI , 就会丧失跨平台特性。 Java堆 对于大多数应用来说Java 堆Java Heap是 Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例几乎所有的对象实例都在这里分配内存。 堆是垃圾收集器管理的主要区域因此很多时候也被称做“GC堆”Garbage Collected Heap。从内存回收的角度来看由于现在收集器基本都采用分代收集算法所以 Java 堆中还可以细分为新生代和老年代再细致一点的有 Eden 空间、From Survivor 空间、To Survivor 空间等。从内存分配的角度来看线程共享的 Java 堆中可能划分出多个线程私有的分配缓冲区Thread Local Allocation Buffer,TLAB。 Java 堆可以处于物理上不连续的内存空间中只要逻辑上是连续的即可当前主流的虚拟机都是按照可扩展来实现的通过 -Xmx 和 -Xms 控制。如果在堆中没有内存完成实例分配并且堆也无法再扩展时将会抛出 OutOfMemoryError 异常。 方法区 方法区Method Area与 Java 堆一样是各个线程共享的内存区域它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分但是它却有一个别名叫做 Non-Heap非堆目的应该是与 Java 堆区分开来。 Java 虚拟机规范对方法区的限制非常宽松除了和 Java 堆一样不需要连续的内存和可以选择固定大小或者可扩展外还可以选择不实现垃圾收集。垃圾收集行为在这个区域是比较少出现的其内存回收目标主要是针对常量池的回收和对类型的卸载。当方法区无法满足内存分配需求时将抛出 OutOfMemoryError 异常。 JDK8 之前Hotspot 中方法区的实现是永久代PermJDK8 开始使用元空间Metaspace以前永久代所有内容的字符串常量移至堆内存其他内容移至元空间元空间直接在本地内存分配。 为什么要使用元空间取代永久代的实现 字符串存在永久代中容易出现性能问题和内存溢出。类及方法的信息等比较难确定其大小因此对于永久代的大小指定比较困难太小容易出现永久代溢出太大则容易导致老年代溢出。永久代会为 GC 带来不必要的复杂度并且回收效率偏低。将 HotSpot 与 JRockit 合二为一。 运行时常量池 运行时常量池Runtime Constant Pool是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外还有一项信息是常量池Constant Pool Table用于存放编译期生成的各种字面量和符号引用这部分内容将在类加载后进入方法区的运行时常量池中存放。 一般来说除了保存 Class 文件中描述的符号引用外还会把翻译出来的直接引用也存储在运行时常量池中。 运行时常量池相对于 Class 文件常量池的另外一个重要特征是具备动态性Java 语言并不要求常量一定只有编译期才能产生也就是并非预置入 Class 文件中常量池的内容才能进入方法区运行时常量池运行期间也可能将新的常量放入池中这种特性被开发人员利用得比较多的便是 String 类的 intern() 方法。 既然运行时常量池是方法区的一部分自然受到方法区内存的限制当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。 直接内存 直接内存Direct Memory并不是虚拟机运行时数据区的一部分也不是 Java 虚拟机规范中定义的内存区域。 在 JDK 1.4 中新加入了 NIO引入了一种基于通道Channel与缓冲区Buffer的 I/O 方式它可以使用 Native 函数库直接分配堆外内存然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能因为避免了在 Java 堆和 Native 堆中来回复制数据。 显然本机直接内存的分配不会受到 Java 堆大小的限制但是既然是内存肯定还是会受到本机总内存包括 RAM 以及 SWAP 区或者分页文件大小以及处理器寻址空间的限制。服务器管理员在配置虚拟机参数时会根据实际内存设置 -Xmx 等参数信息但经常忽略直接内存使得各个内存区域总和大于物理内存限制包括物理的和操作系统级的限制从而导致动态扩展时出现 OutOfMemoryError 异常。 Java内存模型 Java内存模型是共享内存的并发模型线程之间主要通过读-写共享变量堆内存中的实例域静态域和数组元素来完成隐式通信。 Java 内存模型JMM控制 Java 线程之间的通信决定一个线程对共享变量的写入何时对另一个线程可见。 计算机高速缓存和缓存一致性 计算机在高速的 CPU 和相对低速的存储设备之间使用高速缓存作为内存和处理器之间的缓冲。将运算需要使用到的数据复制到缓存中让运算能快速运行当运算结束后再从缓存同步回内存之中。 在多处理器的系统中(或者单处理器多核的系统)每个处理器内核都有自己的高速缓存它们有共享同一主内存(Main Memory)。 当多个处理器的运算任务都涉及同一块主内存区域时将可能导致各自的缓存数据不一致。 为此需要各个处理器访问缓存时都遵循一些协议在读写时要根据协议进行操作来维护缓存的一致性。 JVM主内存与工作内存 Java 内存模型的主要目标是定义程序中各个变量的访问规则即在虚拟机中将变量线程共享的变量存储到内存和从内存中取出变量这样底层细节。 Java内存模型中规定了所有的变量都存储在主内存中每条线程还有自己的工作内存线程对变量的所有操作都必须在工作内存中进行而不能直接读写主内存中的变量。 这里的工作内存是 JMM 的一个抽象概念也叫本地内存其存储了该线程以读 / 写共享变量的副本。 就像每个处理器内核拥有私有的高速缓存JMM 中每个线程拥有私有的本地内存。 不同线程之间无法直接访问对方工作内存中的变量线程间的通信一般有两种方式进行一是通过消息传递二是共享内存。Java 线程间的通信采用的是共享内存方式线程、主内存和工作内存的交互关系如下图所示 这里所讲的主内存、工作内存与 Java 内存区域中的 Java 堆、栈、方法区等并不是同一个层次的内存划分这两者基本上是没有关系的如果两者一定要勉强对应起来那从变量、主内存、工作内存的定义来看主内存主要对应于Java堆中的对象实例数据部分而工作内存则对应于虚拟机栈中的部分区域。 重排序和happens-before规则 在执行程序时为了提高性能编译器和处理器常常会对指令做重排序。重排序分三种类型 编译器优化的重排序。编译器在不改变单线程程序语义的前提下可以重新安排语句的执行顺序。指令级并行的重排序。现代处理器采用了指令级并行技术Instruction-Level Parallelism ILP来将多条指令重叠执行。如果不存在数据依赖性处理器可以改变语句对应机器指令的执行顺序。内存系统的重排序。由于处理器使用缓存和读 / 写缓冲区这使得加载和存储操作看上去可能是在乱序执行。 从 java 源代码到最终实际执行的指令序列会分别经历下面三种重排序 JMM 属于语言级的内存模型它确保在不同的编译器和不同的处理器平台之上通过禁止特定类型的编译器重排序和处理器重排序为程序员提供一致的内存可见性保证。 java 编译器禁止处理器重排序是通过在生成指令序列的适当位置会插入内存屏障重排序时不能把后面的指令重排序到内存屏障之前的位置指令来实现的。 happens-before 从 JDK5 开始java 内存模型提出了 happens-before 的概念通过这个概念来阐述操作之间的内存可见性。 如果一个操作执行的结果需要对另一个操作可见那么这两个操作之间必须存在 happens-before 关系。这里提到的两个操作既可以是在一个线程之内也可以是在不同线程之间。 这里的“可见性”是指当一条线程修改了这个变量的值新值对于其他线程来说是可以立即得知的。 如果 A happens-before B那么 Java 内存模型将向程序员保证—— A 操作的结果将对 B 可见且 A 的执行顺序排在 B 之前。 重要的 happens-before 规则如下 程序顺序规则一个线程中的每个操作happens- before 于该线程中的任意后续操作。监视器锁规则对一个监视器锁的解锁happens- before 于随后对这个监视器锁的加锁。volatile 变量规则对一个 volatile 域的写happens- before 于任意后续对这个 volatile 域的读。传递性如果 A happens- before B且 B happens- before C那么 A happens- before C。 下图是 happens-before 与 JMM 的关系 volatile关键字 volatile 可以说是 JVM 提供的最轻量级的同步机制当一个变量定义为volatile之后它将具备两种特性 保证此变量对所有线程的可见性。而普通变量不能做到这一点普通变量的值在线程间传递均需要通过主内存来完成。 注意volatile 虽然保证了可见性但是 Java 里面的运算并非原子操作导致 volatile 变量的运算在并发下一样是不安全的。而 synchronized 关键字则是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得线程安全的。 禁止指令重排序优化。普通的变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致
http://www.tj-hxxt.cn/news/218267.html

相关文章:

  • 个人网站注册费用谷歌收录wordpress
  • 制作网站先做前端还是后端西安制作网站软件
  • 天猫网站左侧菜单向右滑出的导航菜单做众筹网站要什么资质
  • 清河做网站报价wordpress 微信 插件
  • 中山网站制作工具网站开发与网站设计区别
  • 深圳市城乡住房和建设局网站网站招牌模板
  • 免费名字设计成图案seo教学实体培训班
  • 免费建站网站 百度一下网络规划设计师2022论文5月
  • 深圳制作网站培训机构工程承包去哪个网站
  • 站长工具seo综合查询收费吗珠海网站建设案例
  • 做网站的盈利点用什么做网站开发
  • 网站备案号含义那里有专做粮食的网站
  • 百度网站模板免费下载迅雷资源做下载网站
  • 房地产网站欣赏推广方式有哪些渠道
  • 天元建设集团有限公司法定代表人宁波专业seo团队
  • 国家电网交流建设分公司网站网站域名中文后缀
  • 做电影网站会违法吗湖南人文科技学院全国排名
  • 鲜花网站建设介绍wordpress常规设置
  • 后期网站建设及维护推广丽水 网站建设
  • 南城网站建设价格贵阳网站建设费用多少网帮你
  • 福州网站设计外包上海的咨询公司排名
  • 昌平上门做网站那高级网站开发培训
  • 网站到期如何续费项目计划书ppt
  • 做心理咨询可以在哪些网站发贴深圳房地产信息平台官网
  • 深圳建站网站公司小程序怎么做网站
  • 苏州高端网站设计企业低调赚大钱的灰色行业
  • 龙门石窟网站建设策划报告那家财经网站做的好
  • 网站建设费如何记账如何做返利网站
  • 长沙网站建设网网站开发费入什么费用
  • 一个域名可以做几个网站栾城哪家公司做网站