目录

JVM

java内存模型

Java内存模型主要包括:线程私有的程序计数器、java虚拟机栈、本地方法栈线程共享的堆空间、元数据区、直接内存

  try(FileChannel from = new FileInputStream("D:\\asd\\asd.mp4").getChannel();
      FileChannel to = new FileOutStream("D:\\asd.mp4").getChannel();){
      ByteBuffer bb = ByteBuffer.allocateDirect(1024 * 1024);//读写的缓冲区1Mb,直接内存
      while(true){
          int len = from.read(bb);
          if(len == -1)
              break;
          bb.flip();
          to.write(bb);
          bb.clear();
      }
  }catch(IOException e){
      e.printStackTrace();
  }
  1. 直接内存的分配与回收:直接内存的分配与回收是java通过UnSafe对象来管理的,并且回收需要主动调用freeMemory()方法,不直接收JVM内存回收和管理。(虚引用)
  2. ByteBuffer底层内存分配和释放的过程
    • ByteBuffer被创建时,调用UnSafe对象的allocateMemory(_1GB)方法直接分配内存,返回long base,即内存地址。
    • ByteBuffer被销毁时,调用UnSafe对象的freeMemory(base)方法释放直接内存
    • ByteBuffer内部,使用Cleaner(虚引用)来检测ByteBuffer对象,一旦ByteBuffer对象被GC,那么就会有ReferenceHandler线程通过Cleaner的clean方法调用freeMemory()方法释放直接内存。

JDK工具

  1. javac:javac是Java编译器,用于将Java源代码编译为字节码文件(.class文件),以便在Java虚拟机上运行。

  2. java:java命令用于启动Java应用程序。它接受字节码文件作为输入,并执行应用程序的入口点(即包含main()方法的类)。

  3. jar:jar命令用于创建和管理Java归档文件(JAR文件),它可以将多个类文件、资源文件和库文件打包到一个JAR文件中,并支持压缩和解压缩操作。

  4. javadoc:javadoc工具用于从Java源代码生成API文档。它读取源代码中的特殊注释(Javadoc注释),并根据注释生成HTML格式的文档,描述类、接口、方法和字段的用法和说明。

  5. jdb:jdb是Java调试器,用于在命令行中调试Java程序。它允许开发人员设置断点、单步执行、查看变量值等,帮助定位和修复代码中的问题。

  6. jconsole:jconsole是一个基于JMX的图形化监控工具,用于监视和管理Java应用程序。它提供了实时的性能指标和线程信息,可用于识别性能问题和调优Java应用程序。

  7. jvisualvm:jvisualvm是一个多功能的Java性能分析工具,提供了各种监控和分析功能。它可以用于查看内存使用情况、线程状态、垃圾回收等,帮助开发人员优化和调试Java应用程序。

  8. jmap:jmap命令用于生成Java堆转储快照(heap dump)文件,以便进行内存分析和查找内存泄漏问题。它可以显示堆内存使用情况、对象实例数量和对象类型等信息。

  9. jps:查看java进程信息

  10. jstack:jstack命令用于生成Java应用程序的线程转储快照,以便分析线程状态和死锁问题。它可以显示各个线程的堆栈跟踪信息,帮助定位和解决线程相关的问题。学习学习2

  11. jstat:jstat命令用于监控Java虚拟机的各种统计信息,例如垃圾回收情况、堆内存使用情况、类加载信息等。它可以提供实时的性能数据,用于分析和调整JVM的运行时行为。

  12. jinfo:jinfo命令用于获取Java虚拟机的配置信息,例如系统属性、运行时参数等。它还可以动态修改虚拟机的某些参数,如堆内存大小、GC

  13. jhat:jhat是Java堆分析工具,用于分析和浏览Java堆转储文件(heap dump)。它提供一个Web界面,用于可视化查看堆内存中的对象、引用关系和统计信息,帮助分析内存泄漏和性能问题。

  14. jrunscript:jrunscript是一个命令行工具,用于执行JavaScript脚本。它可以与Java集成,访问Java类和方法,提供了一种在Java环境中运行和测试JavaScript的方式。

  15. jvisualvm:jvisualvm是一个全功能的Java性能监控和分析工具,提供了各种监控和分析功能,包括内存使用情况、线程状态、垃圾回收、CPU使用等。它还支持插件扩展,可以集成其他工具和扩展功能。

  16. jdeps:jdeps是一个依赖关系分析工具,用于分析Java类文件之间的依赖关系。它可以帮助开发人员检查和解决类之间的依赖关系问题,包括循环依赖、缺失依赖等。

  17. jlink:jlink是一个Java模块化工具,用于创建自定义的JRE(Java Runtime Environment)镜像。它可以根据应用程序的需求,裁剪和优化JRE,只包含所需的模块和类库,减少运行时环境的大小。

  18. jshell:jshell是一个交互式的Java编程工具,用于快速测试和尝试Java代码片段。它提供了一个命令行界面,可以即时执行Java代码,并提供实时反馈和结果展示。

  19. jcmd:jcmd是一个通用的JDK命令工具,用于执行各种诊断和管理操作。它可以用于查看运行中的Java进程、生成线程转储、查看性能数据等。

JVM调优

  1. jps: 查看正在运行的java进程id,jps -v查看进程启动时的JVM参数
  2. jstat:查看指定进程的JVM统计信息。
  1. jinfo:实时查看和修改指定进程的JVM配置参数,jinfo -flag查看和修改具体参数
  2. jstack:打印指定进程此刻的线程快照。定位线程长时间停顿的原因,例如死锁(Deadlock)、等待资源(Waiting on condition)、等待获取监视器(Waiting on monitor entry)、阻塞(Blocked)

jstat

❗️important
  • 可以比较Java进程的启动时间(t)以及总GC时间(GCT),或者两个测量的间隔时间(Δt)以及总GC时间的增量(ΔGCT),得出GC时间占运行时间的比例(GCT/t,ΔGCT/Δt)。
  • 如果该比例超过20%,说明目前堆的压力较大;如果超过90%,说明堆里没有可用空间,随时可能抛出OOM异常。
  • 判断内存泄漏:每隔一段较长时间采样多组OU(老年代内存量)的最小值,如果这些最小值在上涨,说明无法回收的对象在不断增加,可能是内存泄漏导致的。

生成dump文件

  1. jmap
  1. VisualVM 首先启动程序
    打开JvisualVM工具
    打开对应的程序进程
    点击线程->线程dump 右键快照->另存为
  2. OOM时自动生成堆转储文件 -XX:+HeapDumpOnOutOfMemoryError:开启自动生成 -XX:HeapDumpPath=/tmp/java_%p_%t.hprof:设置堆转储文件保存位置,以进程ID和事件戳命名 -XX:+HeapDumpBeforeFullGC:在进行FullGC前自动生成堆转储文件(HeapDumpAfterFullGC 实现在Full GC后dump)
    🔥warning

    如果没有开启自动GC,则此参数无效。JDK9之后该参数已被删除

JVM调优参数

❗️important

调优JVM参数主要关注停顿时间(STW)和吞吐量(运行时长/(运行时长+GC时长)),二者不可得兼,提高吞吐量会拉长停顿时间 如果应用程序跟用户基本不交互,就优先提高吞吐量。如果和用户交互频繁,就优先缩短停顿时间。

  1. 常用参数
❗️important
  • JVM调优最有效、最实用的方式是升级垃圾回收器。根据CPU核数,升级当前版本呢支持的最新垃圾回收器
  • CPU单核:SerialGC是唯一选择(-XX:+UseSerialGC
  • CPU多核:关注吞吐量,选择PS+PO(JDK8默认 -XX:+UseParallelOldGC
  • CPU多核:关注停顿时间,JDK1.8以上,JVM可用内存6G以上,选择G1。JDK版本1.6或者1.7,那么选择ParNew+CMS。

内存溢出

  1. 本地直接内存溢出:直接内存设置过小导致溢出
  2. 虚拟机栈和本地方法栈溢出:如果虚拟机栈内存允许动态扩展,并且方法递归层数太深时,导致扩展容量时无法申请到足够内存。
  3. 方法区溢出:运行时生成大量动态类时会内存溢出
  1. 堆溢出:

内存泄漏

内存泄漏9种情况

  1. 静态容器中的对象:静态集合类的生命周期与JVM一致,容器中的对象引用也将一直得到GC。Java中不准静态方法引用非静态方法也是防止内存泄漏
  2. 单例对象引用的外部对象:单例模式中,如果单例对象持有外部对象的引用,因为单例对象不会被回收,这个外部对象也不会被回收
  3. 外部类跟随内部类被引用:内部类持有外部类,内部类对象被长期引用,即使外部类实例对象不再使用,由于内部类持有外部类的实例对象,这个外部类对象也不会被GC。
  4. 数据库、网络、IO连接忘记关闭:不再使用时应显性关闭。
  5. 变量作用域不合理:如果某个变量只在某个方法中使用,却声明为成员变量,被使用后没有赋值为null,导致变量不再使用,但生命周期取仍和对象一致。
  6. HashSet中对象改变哈希值:当一个对象被存储进HashSet后,就不能修改这个对象中那些参与计算哈希值的字段了,否则对象哈希值改变,找不到对应的value
  7. 缓存引用往删除:一旦把对象放入缓存中,缓存忘记删除,引用将一直存在
  8. 逻辑删除而非真实删除:对于监视器和其他回调,如果客户端在API中注册回调,却没有显示取消,那么就会积聚。需要确保回调立即被当作垃圾回收的最佳方式是只保存它的弱引用,例如将其保存为WeakHashMap中的键。例如出栈只是移动了指针,而没有将出栈的位置复制为null,导致已出栈的位置还存在引用。
  9. 线程池ThreadLocal忘记remove():使用线程池时,ThreadLocal需要在使用完线程中的线程变量收到remove(),否则内存泄漏。因为线程执行完后没有销毁而是被线程池回收,导致ThreadLocal中的对象不能被GC