深入理解JVM虚拟机(一)

目录:

(1)JVM概念 

(2)程序计数器-作用

(3)栈

        问题辨析:

        内存溢出:

(4)线程诊断

(5)本地方法栈


(1)JVM概念 

定义: 

 刚开始java源代码Helloword.java,经过javac编译成了class字节码,.class字节码再经使用ava程序加载到java虚拟机里面就可以运行了,jvm就是java程序的运行环境。

好处:

java语言是跨平台的, 正是由于 JVM,JVM屏蔽了字节码跟底层系统之间的差异,对外提供了一个一致的运行环境,jvm最终用解释的方法解释二进制字节码,来达到代码平台的无关性

JVM+基础类库:才能构成完整意义上的java运行时环境,JRE是完整运行时环境的缩写

JVM+基础类库+编译工具(javac等等):java开发工具包 

 

 

 java为二进制字节码,经过类加载器加载到JVM里去运行,类都是放在方法区的,类创建的实例对象都是放在堆的部分,堆的对象当在调用方法时会用到虚拟机栈、程序计数器、本地方法栈

方法执行时是由执行引擎中的解释器逐行进行执行

方法中的热点代码也就是频繁执行的代码会由JIT的及时编译器做编译或者认为优化后的执行

GC:对堆里面不在使用的对象进行垃圾回收

还有一些java代码不方便实现的功能,必须调用底层操作系统的功能,需要跟操作系统的功能打交道,需要借助本地方法接口来调用操作系统提供的一些功能方法

(2)程序计数器-作用

 

 解释器会解释指令为机器码交给 cpu 执行,程序计数器会记录下一条指令的地址行号,这样下一次解释器会从程序计数器拿到指令然后进行解释执行。

 

 程序计数器是寄存器来实现的,寄存器是cpu组件里读取最快的一个单元,因为我们读取指令地址是非常频繁的,所以jvm虚拟机在设立的时候,就把我们cpu的寄存器当做计数器来存储地址,将来去读取这个地址

多线程的环境下,CPU会有一个调度器组件,给他们分配时间片,比如说给线程1分配一个时间片,在这个时间片内如果没有执行完,就会把线程1的状态进行暂存,切换到线程2去,线程2执行完时间片,再切换回来执行线程1剩余的部分。如果两个线程发生了上下文切换,那么程序计数器会记录线程下一行指令的地址行号,以便于接着往下执行。

(3)栈

特点:先进后出

java中的虚拟机栈:线程运行的内存空间,一个线程需要一个栈多个线程会有多个虚拟机栈,一个栈内由多个栈帧组成,一个栈帧对应一次方法的调用,线程是要执行代码的,代码是由一个个方法组成,,每个方法运行需要的内存称为栈帧

 

 Frames:可以理解为虚拟机栈:调用方法,依次进入

 方法2执行结束释放掉:

方法1结束:只剩下一个主方法

 主方法再执行方法执行结束:

 问题辨析:

 

 解答1 否:因为栈内存无非就是一次一次的方法调用,所产生的栈针内存,栈针内存调用结束后,会被弹出栈,会自动的被回收掉,因此不需要垃圾回收来管理我们的栈内存

解答2 否:栈内存可以在运行代码时通过虚拟机参数来指定,不指定Linux、macOS、Oracle默认1024KB。不是,因为物理内存的大小是一定的,比如说,一个线程使用的是栈内存,一个线程使用1兆内存,假设总共的物理内存有500兆,理论上可以有500个线程同时运行,但是给每个线程的栈内存设置2兆的内存,那么只能同时运行250个线程,所以,栈内存并不是越大越好,并不会增强运行效率

 解答3:看这个变量是否线程安全,看这个变量多个线程对这个变量是共享的,还是这个变量对每个线程是私有的

 

多个线程同时执行这个方法,是否造成这个x值的混乱呢?不会,因为x变量是方法内的局部变量,一个线程对应一个栈,线程中每一次方法调用,都会产生一个新的栈帧

这个变量时线程私有的,所以不会有在多线程的环境下会有多线程的影响的问题

 

如果x为static的话,如果不加线程保护的话,会产生线程安全问题。是每个线程共享的,需要考虑线程安全,是每个线程私有的,不用考虑线程安全 

 m1是线程安全的、m2不是线程安全的、m3是线程不是安全的

m2:不是方法的局部变量,而是方法的参数传递进来,有可能有其他的线程能够访问倒它,就不是线程私有的了

 

m3方法sb是局部变量,他作为返回值,逃离了方法的作用范围,有可能被别的线程访问到,出现问题

内存溢出:

在方法的递归调用中没有设置正确的结束条件,就会导致自己调用自己不能结束,就会导致内存溢出 

 

案例: 

 

 

通过虚拟参数,可以设置栈内存小一些,看一看方法递归次数是否会减少

通过-Xss设置参数 

 重新启动:这一次只循环了5222次。导致内存溢出

 

 

 栈帧过大导致内存溢出:这种情况很少出现,因为栈帧里都是局部变量啊、方法参数呀、他们占用的内存都很少

 

 案例二:

 

  

 运行出现了栈内存溢出的错误

 是由于我们引用的两个类循环引用问题导致jaon解析时出现内存溢出,怎么解决呢在json引用时打破循环引用

 给员工类加注解:@JsonIgnore:转换时忽略这个属性转换。把双向关联改为单项关联,只通过部门去关联员工了,员工不在关联部门了

改动成功后再次运行:

 

 (4)线程诊断

 

在Linux中后天运行一下java代码:

使用top命令检测后台进程 对cpu的使用占用情况:

 

发现这个java代码占用了cpu的99.3。top命令只能定位到某个进程,不能定位到某个线程导致cpu占用

使用ps命令查看线程对cpu的占用情况:ps  H  -eo  pid,tid,%cpu

H:把进行的所有线程信息展示出来

-eo:规定输出哪些内容, 比如pid:进程id、tid:线程id

%cpu:对cpu的占用

 是把Linux里面所有的进程、线程、占用情况都输出了

 

 如果数量过多,使用grep进行 筛选已知的进程编号

 

使用jdk提供的工具jstack到底是那个线程出现的问题了

jstack 进程id

 

 它能把这个进程中所有的java线程列出来

 用jstack输出的线程编号是16进制的,需要把十进制的线程编号做一个换算

 发现thread1的nid为7f99,所以问题在thread1,这个线程转态是RUNNABLE,一直在运行,使它占用cpu过多时间片

他还能把具体的java代码行数显示出来,有问题的代码

 然后再查询java源代码,根据它指示的行数:再进行具体的解决

 运行另外一个程序

等了很久没有出现想要的结果,可能是某个线程出现了死锁,如何解决线程死锁的问题呢 

使用jstack 进程id  查看一下:

 

 可以看到线程的信息

发现了死锁 

 在java代码中29行、21行出现死锁

 

 

 (5)本地方法栈

是虚拟机调用本地方法时需要给本地方法提供的一个内存空间,本地方法运行的时候使用的内存叫本地方法栈

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喵俺第一专栏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值