根据字节码判断方法参数的个数

Jvm 是如何确定方法的参数个数的?

找到 Method 的 DescriptionIndex 的属性,找到对应的描述,例如:

这个例子中的 java 代码,add 方法对应的代码是 (II)I,最后一个 I 代表返回值,这个代表两个整型的参数.

​同样,(IILjava/lang/String;Z)I 代表有4个参数,字符串的表示是:Ljava/lang/String;,解析比较特殊。

0

如何优化Tomcat服务器的利用率

要想优化Tomcat的利用率,可以从下面的示例开始:

我们可以想象Tomcat是一个瓶子,如果想在其中放更多的水,并在几秒钟内尽快倒出水,则需要考虑两个因素:

  • 瓶颈的大小。
  • 瓶子本身的体积。

同样,调整Tomcat服务器时,需要关注两个因素。

  • 连接器。 –>瓶颈,在server.xml中定义。
  • JVM(最大内存池)。 –>内存,在Java选项中定义。

首先,取消下面行的注释,使用自定义的executor。默认情况下,executor是禁用的。

通常,maxThreads=”500″ minSpareThreads=”20″ maxIdleTime=”60000″ 这几个就够了。

然后更改连接超时值。

至于jvm优化,以后再说。

0

java里的jvm是用什么语言写的

JAVA里的jvm虚拟机用的是C语言+汇编语言开发的。在此之上就是JAVA本身了,虚拟机是起到解析作用。

另外,JAVA并不比C语言慢,说JAVA慢一般是九十年代那时候的JAVA。

现在一段优秀的JAVA程序和C程序执行效率上来比较是没有多大差距的。并且现在JAVA已经可以像C语言那样,直接编译为可执行文件(不用虚拟机,跨平台为代价)了。

如若看过卓越编程之道二(运用底层思维编写高级代码),那里面就详细的讲述了高级语言从编写到编译执行的过程,通过目标文件的反汇编对比,发现C,C++,JAVA,dephi等语言在同等质量下的目标文件长度上基本上没多大区别,一门语言的运行速度快慢,与你编写代码过程中是否符合编译器规则息息相关。

Java底层实现是用C语言写的,因为做了很多封装,所以比C语言速度慢?

JVM是c写的,所以对JVM常用的攻击就是buffer overflow。

c语言写的, java6.0都已经开源了。

在windows平台的JVM实现是用VC写的,我们平常下载的JDK都有一个src.zip,那就是Java的源码 。

原始是用C写的,如javac命令等,后面的功能是java自身写的,如api,现在大多都开源了,有兴趣可以看看叫openjdk的开源项目,你也可以提供代码,说不定后续版本会采用。

+1

Tomcat配置JVM参数(包括单独配置和在eclipse、idea里配置)

环境

Tomcat8.5,jdk8

配置参数说明

-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize:设置年轻代大小
-XX:NewRatio:设置年轻代和年老代的比值
-XX:SurvivorRatio:年轻代中Eden区与两个Survivor区的比值
-XX:PermSize:设置持久代大小
-XX:ParallelGCThreads:设置并行收集器收集时使用的CPU数,并行收集线程数

配置说明

情况一:在catalina.bat或catalina.sh文件里配置

Linux环境

找到 tomcat 的 bin 目录下的 catalina.sh 文件,在第110行 # OS specific support. $var must be set to either true or false.后,也就是shell代码开头,添加如下参数

检验是否生效

运行startup.sh启动tomcat,输入如下命令

如下图所示,JVM启动参数和我们添加的参数一样,说明有效。

Windows环境

找到 tomcat 的 bin 目录下的 catalina.bat 文件,在第127行 set "CURRENT_DIR=%cd%" 后,添加设置JVM参数代码

检验是否生效

运行startup.bat启动tomcat,然后命令行窗口中输入命令jvisualvm(打开JDK自带的JVM工具Java Visual),查看tomcat的JVM参数。

如下图所示,JVM启动参数和我们添加的参数一样,说明有效。

情况二:在setenv.bat或者setenv.sh文件里配置

setenv.bat或者setenv.sh是tomcat的变量通用文件,里面的变量可以被多种启动器调用,可以被daemon.sh和startup.sh等启动器引用。如果tomcat的bin目录下没有这个文件,可以手动创建。以下是setenv.sh样本

这边我们可以加上如下代码,方便查看

检测是否生效

运行catalina.sh,查看日志,可以看到配置的信息

windows下就是在setenv.bat文件里加上如下代码

情况三:Eclipse中修改tomcat配置jvm参数

修改1:

在Eclipse中下面Servers双击Tomcat Server… 然后点击General InformAtion 下的Open launch configuration;

会弹出Edit Configuration,然后在选中Arguments选项卡;在VM arguments文本框中最后面添加

具体如何设置自行参考

修改2:

在Eclipse菜单栏中Window ——》Preferences ——》Server ———》 Runtime Environment

选择您用的Tomcat 然后点击Edit…弹出Edit Server Runtime Ecvironment 下面JRE选项后面的Installed JREs…

点击弹出Installed JREs;在选中您用的Jre在点击Edit..在Defaul VM Arguments:中填入-Xms256m -Xmx512m(具体如何设置自行参考)

情况四:Idea中修改tomcat配置jvm参数

补充说明

上面的jvm参数设置只是举个例子,不一定是最好的,大家自行根据实际情况决定如何配置。

0

深入理解Java内存模型(JMM)

前言

Java内存模型是Java程序员学习JVM前必须要掌握的基础知识 ,也是面试中经常会被问到的问题点。但是要真正完全弄清楚它,还是有点难度的,因为Java内存模型是不可见的,它并不是一个真实的东西,它只是一个概念、一个规范。

计算机硬件体系介绍

CPU多级缓存

要想完全搞清楚Java内存模型,先要了解计算机硬件架构,特别是计算机CPU和主存之间的架构。

在计算机中,cpu和内存的交互最为频繁,相比内存,磁盘读写太慢,内存相当于高速的缓冲区。

但是随着cpu的发展,内存的读写速度也远远赶不上cpu。因此cpu厂商在每颗cpu上加上高速多级缓存,用于缓解这种情况。现在cpu和内存的交互大致如下。

CPU多级缓存

三级缓存(L1、L2、L3),L1最靠近CPU核心,L2其次,L3再次。运行速度方面:L1最快、L2次快、L3最慢;容量大小方面:L1最小、L2较大、L3最大。CPU会先在最快的L1中寻找需要的数据,找不到再去找次快的L2,还找不到再去找L3,L3都没有那就只能去内存找了。

其中一级缓存还分为一级数据缓存(Data Cache,D-Cache,L1d)和一级指令缓存(Instruction Cache,I-Cache,L1i),分别用于存放数据及执行数据的指令解码,两者可同时被CPU访问,减少了CPU多核心、多线程争用缓存造成的冲突,提高了处理器的效能。一般CPU的L1i和L1d具备相同的容量。

我的电脑的CPU缓存

我的电脑的CPU缓存

在多核cpu中,每个处理器都有各自的高速缓存(L1,L2,L3),而主内存确只有一个。以我的电脑为例,因为cpu成本高,缓存区一般也很小。简单粗暴理解就是L1越大CPU就越贵。

缓存一致性问题

给CPU加上了高速缓存,主内存存取速度赶不上CPU的问题貌似就解决了,一切看似都很美好。但是科技在不断进步,CPU厂商们也在不断推陈出新,于是多核CPU出现了,每个CPU上又有高速缓存,CPU与内存的交互就变的比原来复杂了,如下图所示。

多核CPU与主存

这就引发了新的问题,缓存一致性问题。为什么会出现这个问题呢?

CPU需要修改某个数据,是先去Cache中找,如果Cache中没有找到,再去内存中找,然后把数据复制到Cache中,下次就不需要再去内存中寻找了,然后进行修改操作。而修改操作的过程是先在Cache里面修改数据,然后再把数据刷新到主内存。

其他CPU需要读取数据,也是先去Cache中去寻找,如果找到了就不会去内存找了。 所以当两个CPU的Cache同时都拥有某个数据,其中一个CPU修改了数据,另外一个CPU是无感知的,并不知道这个数据已经不是最新的了,它要读取数据还是从自己的Cache中读取,这样就导致了“缓存不一致”。

其实对于这样的描述并不是十分准确,因为计算、读取等操作都是在CPU的寄存器中进行的,这样的描述是为了让问题变得更简单,相信学过计算机体系的人应该非常清楚整个流程,在这里就简单的描述下。

如何解决 “缓存不一致” ?

解决这个问题的方法有很多,比如:

1.总线加锁(此方法性能较低,现在已经不会再使用)。
2.MESI协议: 这是Intel提出的,MESI协议也是相当复杂,在这里我就简单的说下:当一个CPU修改了Cache中的数据,会通知其他缓存了这个数据的CPU,其他CPU会把Cache中这份数据的Cache Line置为无效,要读取数据的话,直接去内存中获取,不会再从Cache中获取了。

下图很好的阐释了多核CPU下, MESI协议是如何工作的。

MESI协议 ,四种数据状态:Modify、Exclusive、Shared、Invalid

有了 MESI协议 ,我们再来看看多核CPU缓存与主内存的关系。

MESI协议下的多核CPU和主存的结构图

Java线程与硬件处理器

其实,我们在Java中开启一个线程,最终也是交给CPU去执行。 具体的流程是:我们在使用Java线程,内部会调用操作系统(OS)的内核线程(Kernel-Level Thread),这种线程是操作系统内核(Kernel)直接支持的,内核通过调度器,对线程进行调度,并将线程交给各个CPU内核去处理。 如下图所示:

Java线程与硬件处理器

Java内存模型

终于到了本文的正题了,有了上面对一些基础知识的了解,接下来学习Java内存模型就会事半功倍。

Java内存模型概念

Java内存模型(jmm, Java Memory Model):JMM规范了java虚拟机与计算机内存是如何协同工作的,规定了一个线程如何和何时可以看到其他线程修改过的共享变量的值,以及在必须时如何同步的访问共享变量。

JMM是java虚拟机规范定义的,用来屏蔽掉java程序在各种不同的硬件和操作系统对内存的访问的差异,这样就可以实现java程序在各种不同的平台上都能达到内存访问的一致性。

正如文章开头所说JMM其实是不存在的,它只是一个规范,最终Java程序都会交给CPU去运行,所以上面是计算机硬件体系是基础,有了上面的基础,才有了Java内存模型,或者说Java的内存模型就是利用了计算机硬件体系。

JVM线程栈和堆结构图

本地内存:我们知道,Java里面每个线程都有一个自己的本地内存(上图绿色区域),存放的是私有变量和主内存数据的副本。如果私有变量是基本数据类型,则直接存放在本地内存,如果是引用类型变量,存放的是引用(指针),实际的数据存放在主内存。本地内存是不共享的,只有属于它的线程可以访问。也有好多人把本地内存称之为线程栈或者工作空间

主内存:存放的是共享的数据,所有线程都可以访问。当然它也有不少其他称呼,比如堆内存共享内存等等。

Java内存模型规定了所有对共享变量的读写操作都必须在本地内存中进行,需要先从主内存中拿到数据,复制到本地内存,然后在本地内存中对数据进行修改,再刷新回主内存。

Java内存模型和计算机硬件架构之间的关系

通过前面的铺垫,我们应该认识到Java的执行最终还是会交给CPU去处理,但是Java的内存模型和硬件架构又不完全一致。对于硬件来说,只有CPU,Cache和主内存,并没有Java内存模型中本地内存(线程栈、工作空间)或者主内存(共享内存,堆内存)的概念。

计算机CPU、缓存与主存结构图

所以不管是Java内存模型中的本地内存,还是主内存的数据,最终都会存储在CPU(更准确的来说 是寄存器)、Cache、内存上。

所以,Java内存模型和计算机硬件架构存在这样的关系:

至此,我们算是了解了 Java内存模型是干什么的。 Java内存模型就是为了解决多线程对共享数据的读写一致性问题。



Java内存模型抽象结构图

Java内存模型的同步操作与规则

知道了Java内存模型是干什么的,那接下来就要了解他是怎么做到这么牛逼的,也就是它的实现原理。

Java内存模型的同步八种操作

Lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。 •Unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其它线程锁定。
Read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,便于后面的load动作使用。
Load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
Use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎。
assign((赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量。
Store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
Write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

Java内存模型的同步规则

•如果要把一个变量从主内存中复制到工作内存中,就需要顺序的执行read和load操作,如果把变量从工作内存同步到主内存中,就需要顺序的执行store和write操作。但Java内存模型只要求上述操作必须是按顺序执行,而没有要求是连续执行。
•不允许read和load、 store和write操作之一单独出现
•不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须同步到主内存中。
•不允许一个线程无原因的(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
•一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即对一个变量实施use和store操作之前,必须先执行assign和load操作。
•一个变量在同一时刻只允许一条线程对其进行lock操作,但是lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现。
•如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值。
•如果一个变量事先没有被lock操作,则不允许对它执行unlock。也不允许unlock一个被其他线程锁定的变量。
•对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。

Java并发内存模型的实质

看了上面的Java内存模型的同步操作与规则,是不是感觉有点似曾相识,不错,它就是围绕着Java并发过程中如何处理原子性、可见性和顺序性这三个特征来设计的。

这三大特性,相信只要是Java程序员,没有人不知道,也是面试中经常被问到的题目。所以有必要再拿出来看看。

并发编程中三个重要特性

这三大特性是非常重要的, 面试的时候很多问题都和它们相关,但是由于篇幅有限,所以下面关于它们太细节的东西我就不写了,只列出一些非常重要的面试关键词,大家根据这些关键词自行去找资料学习吧。

原子性

不可分割,同生共死。
i=1具有原子性,直接在本地内存中进行赋值操作。
而i++则不具有原子性,它有三个步骤:
①把i读取出来(原子性)
②在本地内存中做自增运算(原子性)
③再把值写回i(原子性)多个原子性操作组合在一起,就不具有原子性了。
一般情况下,基本数据类型的赋值,读取都是具有原子性的。

原子性的关键词: atomic包、CAS、CAS的ABA问题、 LongAdder和AtomicLong的区别比较、 synchronized、lock

可见性

一个线程在本地内存中修改了共享内存的数据,对于其他持有该数据的线程是“不可见”的。

导致共享变量在线程间不可见的原因

1.线程交叉执行
2.重排序结合线程交叉执行
3.共享变量更新后的值没有在工作内存与主存间及时更新

可见性关键词: volatile

有序性

代码在运行的时候,执行顺序可能并不是严格从上到下执行的,会进行指令重排。根据CPU流水线作业,一般来说简单的操作会先执行,复杂的操作后执行。
指令重排会有两个规则:
as-if-seria:不管怎么重排序,单线程的执行结果不能发生改变。正是由于这个特性,在单线程中,程序员一般无需理会重排序带来的问题。
happens-before原则

有序性关键词: 指令重排 、 happens-before

参考内容

1.慕课网《Java并发编程》视频教程
2.《深入理解Java虚拟机》

+2

高级Java深入学习JVM书籍推荐

一、《深入理解Java虚拟机——JVM高级特性与最佳实践(第2版)》

基于最新JDK1.7,围绕内存管理、执行子系统、程序编译与优化、高效并发等核心主题对JVM进行全面而深入的分析,深刻揭示JVM的工作原理。以实践为导向,通过大量与实际生产环境相结合的案例展示了解决各种常见JVM问题的技巧和实践。

二、《揭秘Java虚拟机:JVM设计原理与实现》

《揭秘Java虚拟机:JVM设计原理与实现》从源码角度解读HotSpot的内部实现机制,主要包含三大部分——JVM数据结构设计与实现、执行引擎机制及内存分配模型。数据结构部分包括Java字节码文件格式、常量池解析、字段解析、方法解析。每一部分都给出详细的源码实现分析,例如字段解析一章,从源码层面详细分析了Java字段重排、字段继承等关键机制。

三、《Java虚拟机规范》(Java SE 8版)

本书由该技术的创立人所写,是权威的Java虚拟机参考资料。书中完备、准确而又详尽地描述了Java虚拟机。它完整地讲述了由JavaSE8所引入的新特性,例如对包含默认实现代码的接口方法所做的调用,以及为支持类型注解及方法参数注解而对class文件格式所做的扩展。此书也阐明了class文件中各属性的含义,以及字节码验证的规则。

四、《实战Java虚拟机:JVM故障诊断与性能优化》

《实战Java虚拟机——JVM故障诊断与性能优化》内容简介:随着越来越多的第三方语言(Groovy、Scala、JRuby等)在Java虚拟机上运行,Java也俨然成为一个充满活力的生态圈。本书将通过200余示例详细介绍Java虚拟机中的各种参数配置、故障排查、性能监控以及性能优化。

五、《HotSpot实战》

《HotSpot实战》深入浅出地讲解了HotSpot虚拟机的工作原理,将隐藏在它内部的本质内容逐一呈现在读者面前,包括OpenJDK与HotSpot项目、编译和调试HotSpot的方法、HotSpot内核结构、Launcher、OOP-Klass对象表示系统、链接、运行时数据区、方法区、常量池和常量池Cache、Perf Data、Crash分析方法、转储分析方法、垃圾收集器的设计演进、CMS和G1收集器、栈、JVM对硬件寄存器的利用、栈顶缓存技术、解释器、字节码表、转发表、Stubs、Code Cache、Code生成器、JIT编译器、C1编译器、编译原理、JVM指令集实现、函数的分发机制、VTABLE和ITABLE、异常表、虚拟机监控工具(如jinfo、jstack、jhat、jmap等)的实现原理和开发方法、Attach机制、基于GUI的JVM分析工具(如MAT、VisualVM)等内容。

六、《深入理解JVM & G1 GC》

本书主要为学习Java语言的学生、初级程序员提供GC的使用参考建议及经验,着重介绍了G1 GC。

七、《垃圾回收的算法与实现》

本书分为“算法篇”和“实现篇”两大部分。算法篇介绍了标记-清除算法、引用计数法、复制算法、标记-压缩算法、保守式GC、分代垃圾回收、增量式垃圾回收、RC Immix算法等几种重要的算法;实现篇介绍了垃圾回收在Python、DalvikVM、Rubinius、V8等几种语言处理程序中的具体实现。

八、《Java虚拟机精讲》

HotSpot VM 是目前市面上高性能JVM 的代表作之一,它采用解释器+JIT 编译器的混合执行引擎,使得Java 程序的执行性能从此有了质的飞跃。本书以极其精练的语句诠释了HotSpot VM 的方方面面,比如:字节码的编译原理、字节码的内部组成结构、通过源码的方式剖析HotSpot VM 的启动过程和初始化过程、Java 虚拟机的运行时内存、垃圾收集算法、垃圾收集器(重点讲解了Serial 收集器、ParNew 收集器、Parallel 收集器、CMS(Concurrent-Mark-Sweep)收集器和G1(Garbage-First)收集器)、类加载机制,以及HotSpot VM 基于栈的架构模型和执行引擎(解释器的工作流程、JIT 编译器的工作流程、分层编译策略、热点探测功能)等技术。

九、《虚拟机设计与实现 以JVM为例》

本书从一位虚拟机(VM)架构师的角度,以易于理解、层层深入的方式介绍了各种主题和算法,尤其是不同VM通用的主要技术。这些算法用图示充分解释,用便于理解的代码片段实现,使得这些抽象概念对系统软件工程师而言具像化并可编程。书中还包括一些同类文献中较少涉及的主题,例如运行时辅助、栈展开和本地接口。本书集理论性与实践性于一身,不仅结合了高层设计功能与底层实现,而且还结合了*级主题与商业解决方案,是VM设计和工程实践方面的理想参考读物。

0