某某茶叶有限公司欢迎您!
金沙棋牌在线 > 金沙棋牌在线 > JVM执行的分析

JVM执行的分析

时间:2019-12-29 06:38

本 系列 中以前的文章介绍了如何通过以下方式实现并发性:

转载于:转载于:

Java运行流程

图片 1

实际上当JVM将所需要的.class文件加载到JVM进程之中,那么这个过程就需要有一个类加载器(ClassLoader),有了类加载器的好处在于:可以随意指定程序.class文件的所在路径。

JVM:Java虚拟机,所有的程序都要求运行在JVM上,是因为考虑到了可移植性问题,可是如果要想真正去执行程序,你绝对不可能离开操作系统的支持。在Java里面可以使用native实现本地C函数的调用。但是这些都属于程序运行的辅助手段,而真正的程序运行都在“运行时数据区”之中

Akka-http的客户端连接模式除Connection-Level和Host-Level之外还有一种非常便利的模式:Request-Level-Api。这种模式免除了连接Connection的概念,任何时候可以直接调用singleRequest来与服务端沟通。下面我们用几个例子来示范singleRequest的用法:

  • 并行地在多个数据集上执行相同的操作(就像 Java 8 流一样)
  • 显式地将计算构建成异步执行某些操作,然后将结果组合在一起(就像 future 一样)。

处理器速度数十年来一直持续快速发展,并在世纪交替之际走到了终点。从那时起,处理器制造商更多地是通过增加核心来提高芯片性能,而不再通过增加时钟速率来提高芯片性能。多核系统现在成为了从手机到企业服务器等所有设备的标准,而这种趋势可能继续并有所加速。开发人员越来越需要在他们的应用程序代码中支持多个核心,这样才能满足性能需求。

运行时数据区

图片 2

在整个的运行时数据区之中,分为如下几块内存空间:

  • 堆内存:保存所有引用数据类型的真实信息;

  • 栈内存:基本类型、运算、指向堆内存的指针;

  • 方法区:所有定义的方法的信息都保存在方法区之中,此区属于共享区;

![](https://upload-images.jianshu.io/upload_images/3347487-2c5ee4a303d47c95.jpg)
  • 程序计数器:
    是一个非常小的内存空间,小的可以忽略;

图片 3

  • 本地方法栈:
    每一次执行递归的方法处理的时候实际上都会将上一个方法入栈;

图片 4

在整个Java之中存在有对象池的概念,对象池是对整个常量池的一个规则破坏,因为在JVM启动的时候所有的常量都已经分配好内存空间了,但是String中的intern()方法却可以打破这种限制,动态的进行常量池的内容设置

  (for {    response <- Http().singleRequest(HttpRequest(method=HttpMethods.GET,uri="http://localhost:8011/message"))    message <- Unmarshal(response.entity).to[String]  } yield message).andThen {    case Success => println(s"Received message: $msg")    case Failure => println(s"Error: ${err.getMessage}")  }.andThen {case _ => sys.terminate()}

这两种方法都是实现并发性的不错方式,但是您必须将它们明确地设计到应用程序中。

在本系列文章中,您将了解一些针对 Java 和 Scala 语言的并发编程的新方法,包括 Java 如何将 Scala 和其他基于 JVM 的语言中已经探索出来的理念结合在一起。第一期文章将介绍一些背景,通过介绍 Java 7 和 Scala 的一些最新技术,帮助了解 JVM 上的并发编程的全景。您将了解如何使用 Java ExecutorService 和 ForkJoinPool 类来简化并发编程。还将了解一些将并发编程选项扩展到纯 Java 中的已有功能之外的基本 Scala 特性。在此过程中,您会看到不同的方法对并发编程性能有何影响。后续几期文章将会介绍 Java 8 中的并发性改进和一些扩展,包括用于执行可扩展的 Java 和 Scala 编程的 Akka 工具包。

对象访问模式

Java的引用类型是最为重要的数据处理模型,而整个的引用数据类型数据处理之中会牵扯到:堆内存、栈内存、方法区。

所以下面以一个最简单的程序代码为主:

“Object obj = new Object()”

实例化了一个Object类对象:

  • “Object obj”:描述的是保存在栈内存之中,而保存有堆内存的引用,这个数据会保存在本地变量表中;

  • “new Object()”:一个真正的对象,对象保存在堆内存之中。直观的思路整个引用的操作:

  • 新定义的对象的名称保存在本地变量表,而后在这块区域里面需要确定要与之对应的栈内存空间

  • 通过变量表中的栈地址可以找到堆内存

  • 利用堆内存的对象进行本地方法的调用(方法区)

对于所有引用数据类型的访问实际上是存在有两种模式的

  • 通过句柄访问
![](https://upload-images.jianshu.io/upload_images/3347487-a24a9c1b0b0bd5a8.jpg)
  • 通过直接指针访问

图片 5

但是在Java之中它直接利用的是对象保存模式,也就是说堆内存里面不再需要保存句柄,而直接保存具体的对象。就相当于省略了句柄到对象间的查找。而后这个对象可以直接进行Java方法区的调用。

总结:引用数据类型在HotSpot中都是直接进行的引用处理,没有句柄池的概念。

这是一个GET操作:用Http().singleRequest直接把HttpRequest发送给服务端uri并获取返回的HttpResponse。我们看到,整组函数的返回类型都是Future[?],所以用for-comprehension来把所有实际运算包嵌在Future运算模式内。下面这个例子是客户端上传数据示范:

在本文和接下来的几篇文章中,我将着重介绍一种不同的并发性实现方法,该方法基于一种特定的程序结构,与显式编码方法不同。这种程序结构就是 actor 模型。您将了解如何使用 actor 模型的 Akka 实现。(Akka 是一个构建并发和分布式 JVM 应用程序的工具包和运行时。)请参阅 参考资料,获取本文完整示例代码的链接。

Java 并发性支持
在 Java 平台诞生之初,并发性支持就是它的一个特性,线程和同步的实现为它提供了超越其他竞争语言的优势。Scala 基于 Java 并在 JVM 上运行,能够直接访问所有 Java 运行时(包括所有并发性支持)。所以在分析 Scala 特性之前,我首先会快速回顾一下 Java 语言已经提供的功能。

 (for {    entity <- Marshal("Wata hell you doing?").to[RequestEntity]    response <- Http().singleRequest(HttpRequest(method=HttpMethods.PUT,uri="http://localhost:8011/message",entity=entity))    message <- Unmarshal(response.entity).to[String]  } yield message).andThen {    case Success => println(s"Received message: $msg")    case Failure => println(s"Error: ${err.getMessage}")  }.andThen {case _ => sys.terminate()}

Actor 模型基础知识

用于并发计算的 actor 模型基于各种称为 actor 的原语来构建系统。Actor 执行操作来响应称为消息 的输入。这些操作包括更改 actor 自己的内部状态,以及发出其他消息和创建其他 actor。所有消息都是异步交付的,因此将消息发送方与接收方分开。正是由于这种分离,导致 actor 系统具有内在的并发性:可以不受限制地并行执行任何拥有输入消息的 actor。

在 Akka 术语中,actor 看起来就像是某种通过消息进行交互的行为神经束。像真实世界的演员一样,Akka actor 也需要一定程度的隐私。您不能直接将消息发送给 Akka actor。相反,需要将消息发送给等同于邮政信箱的 actor 引用。然后通过该引用将传入的消息路由到 actor 的邮箱,以后再传送给 actor。Akka actor 甚至要求所有传入的消息都是无菌的(或者在 JVM 术语中叫做不可变的),以免受到其他 actor 的污染。

与一些真实世界中演员的需求不同,Akka 中由于某种原因而存在一些看似强制要求的限制。使用 actor 的引用可阻止交换消息以外的任何交互,这些交互可能破坏 actor 模型核心上的解耦本质。Actor 在执行上是单线程的(不超过 1 个线程执行一个特定的 actor 实例),所以邮箱充当着一个缓冲器,在处理消息前会一直保存这些消息。消息的不可变性(由于 JVM 的限制,目前未由 Akka 强制执行,但这是一项既定的要求)意味着根本无需担心可能影响 actor 之间各种共享的数据的同步问题;如果只有共享的数据是不可变的,那么根本不需要同步。

Java 线程基础
在 Java 编程过程中创建和使用线程非常容易。它们由 java.lang.Thread 类表示,线程要执行的代码为 java.lang.Runnable 实例的形式。如果需要的话,可以在应用程序中创建大量线程,您甚至可以创建数千个线程。在有多个核心时,JVM 使用它们来并发执行多个线程;超出核心数量的线程会共享这些核心。

以上是个PUT操作。我们需要先构建数据载体HttpEntity。格式转换函数Marshal也返回Future[HttpEntity],所以也可以包含在for语句内。关注一下这个andThen,它可以连接一串多个monadic运算,在不影响上游运算结果的情况下实现一些副作用计算。值得注意的是上面这两个例子虽然表现形式很简洁,但我们无法对数据转换过程中的异常及response的状态码等进行监控。所以我们应该把整个过程拆分成两部分:先获取response,再具体处理response,包括核对状态,处理数据等:

开始实现

现在您已大体了解了 actor 模型和 Akka 细节,是时候看些代码了。使用 hello 作为编码示例司空见惯,但它确实能够帮助用户快速、轻松地理解一种语言或系统。清单 1 显示了 Scala 中的一个 Akka 版本。

Java 5:并发性的转折点
Java 从一开始就包含对线程和同步的支持。但在线程间共享数据的最初规范不够完善,这带来了 Java 5 的 Java 语言更新中的重大变化 (JSR-133)。Java Language Specification for Java 5 更正并规范化了 synchronized