• [Java] jvm输出到终端用什么编码?
    System.out.println( "Hello World!天" ); 在linux终端,正常显示中文,但改变linux的locale为gbk后就显示乱码了。我还以为JVM是根据系统默认编码来输出呢!又在windows下测试,发现了更难以解释的结果:https://segmentfault.com/q/1010000037689821
  • [Java] 常用的 jvm 调优的参数
     常用的 jvm 调优的参数-Xms2g:初始化推大小为 2g;-Xmx2g:堆最大内存为 2g;-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;-XX:+PrintGC:开启打印 gc 信息;-XX:+PrintGCDetails:打印 gc 详细信息。
  • [Java] jvm 的垃圾回收器
     jvm 的垃圾回收器Serial:最早的单线程串行垃圾回收器。Serial Old:Serial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。ParNew:是 Serial 的多线程版本。Parallel 和 ParNew 收集器类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量。Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。
  • [Java] JVM的生命周期
    JVM的生命周期jvm的生命周期主要包含三个部分,虚拟机的启动,虚拟机的运行,还有虚拟机的退出。虚拟机的启动       java虚拟机的启动是通过引导类加载器(boostrap class loader)创建一个初始类来完成,这个类是由虚拟机的具体实现指定的。虚拟机的运行       执行一个java程序的时候,其实执行的是一个java虚拟机进程虚拟机的退出有如下几种情况程序正常执行退出程序在执行中遇到异常或者错误而异常终止由于操作系统出现错误导致Java虚拟机进程终止某线程调用Runtime类或者System类的exit方法,或者Runtime类的halt方法,并且Java安全管理器也允许这次exit或者halt操作除此之外,JNI规范描述用JNI Invocation API来加载或者卸载Java虚拟机时,Java虚拟机的退出情况
  • [技术干货] 2020-09-24:jvm监控系统是通过jmx做的么?
    2020-09-24:jvm监控系统是通过jmx做的么?#福大大架构师每日一题#
  • [Java] jvm的主要组成部分
    类加载器运行时数据区执行引擎本地库接口
  • [Java] jvm的垃圾回收器
    标记-清除算法标记-整理算法复制算法分代算法
  • [Java] jvm的运行时数据区
    程序计数器虚拟机栈本地方法栈
  • [Java] jvm的垃圾算法
    标记-清除算法;标记-整理算法;复制算法;分代算法。
  • [技术干货] JVM监控java.lang.management简介
        java.lang.management是JDK自带的一套工具库。通过工厂类ManagementFactory对外提供一套管理接口,提供JVM和JVM所运行操作系统的各种信息,例如:内存使用情况、GC情况和操作系统版本等。基于以上信息,可以辅助我们对定位问题或者性能调优提供数据支撑。Management同时允许从本地和远程(JMX)对正在运行的JVM进行监视和管理。MXBean简介      根据ManagementFactory的javadoc,可以看出它提供了大量的工厂方法,使得我们可以通过调用这些方法来获取JVM各组件相关的JavaBean,通过这些JavaBean就可以获取到对应组件的运行状态数据。而这些JavaBean是各个服务启动时自动注册好的,运行数据也是提前计算好的,所以我们并不会因为获取相关数据,而导致额外消耗资源进行计算。       这些JavaBean被称之为MBean或者MXBean。MBean和MXBean两者的区别在于:MXBean是一个特殊的MBean,它的数据类型被限制为开放类型,基本上是原始类型、字符串及其组合。由于这些限制,MXBean可以在不加载类的情况下使用,这使得它们甚至可以与非Java客户机进行互操作。这个特性对于我们进行通用封装是十分重要的。使用案例1:      JVM频繁的FullGC或者Java服务发生了OOM,但是JVM进程并没有自动退出。这种场景下,仅仅在操作系统层面对JVM进程进行监控是不能及时发现相关问题的。可以使用MemoryMXBean和GarbageCollectorMXBean来获取相关信息;并结合其他监控或者定时执行机制,实现告警等功能。实现代码:public class MemoryMXBeanTest {    private static volatile MemoryMXBean memoryMBean;    private static volatile List<GarbageCollectorMXBean> garbageCollectorMXBeanList;     /**     *  获取memoryMBean     */    private static void initMemoryMXBean() {        synchronized (MemoryMXBeanTest.class) {            if (memoryMBean == null) {                try {                    memoryMBean = AccessController.doPrivileged(                            new PrivilegedExceptionAction<MemoryMXBean>() {                                @Override                                public MemoryMXBean run() throws DrsException {                                    return ManagementFactory.getMemoryMXBean();                                }                            });                } catch (Exception exp) {                    log.error("", exp);                }            }        }    }     /**     * 获取GarbageCollectorMXBean     */    private static void initGarbageCollectorMXBean() {        synchronized (MemoryMXBeanTest.class) {            if (garbageCollectorMXBeanList == null) {                try {                    garbageCollectorMXBeanList = AccessController.doPrivileged(                            new PrivilegedExceptionAction<List<GarbageCollectorMXBean>>() {                                @Override                                public List<GarbageCollectorMXBean> run() throws DrsException {                                    return ManagementFactory.getGarbageCollectorMXBeans();                                }                            });                } catch (Exception exp) {                    log.error("", exp);                }            }        }    }     public static MemoryMXBean getMemoryMBean() {        if (memoryMBean == null) {            initMemoryMXBean();        }        return memoryMBean;    }     public static List<GarbageCollectorMXBean> getGCMXBeanList() {        if (garbageCollectorMXBeanList == null) {            initGarbageCollectorMXBean();        }        return garbageCollectorMXBeanList;    }     private Map<String, Object> getMemStatus() {        Map<String, Object> memStatus = new HashMap<String, Object>();        // 堆内存        memStatus.put("Heap mem Committed", getMemoryMBean().getHeapMemoryUsage().getCommitted());        memStatus.put("Heap mem Init", getMemoryMBean().getHeapMemoryUsage().getInit());        memStatus.put("Heap mem Max", getMemoryMBean().getHeapMemoryUsage().getMax());        memStatus.put("Heap mem Used", getMemoryMBean().getHeapMemoryUsage().getUsed());        // 堆内存使用率        memStatus.put("Heap mem Used Rato",                (getMemoryMBean().getHeapMemoryUsage().getUsed() * 100 / getMemoryMBean().getHeapMemoryUsage().getMax()) + "%");         // 堆外内存        memStatus.put("Non-Heap mem Committed", getMemoryMBean().getNonHeapMemoryUsage().getCommitted());        memStatus.put("Non-Heap mem Init", getMemoryMBean().getNonHeapMemoryUsage().getInit());        memStatus.put("Non-Heap mem Used", getMemoryMBean().getNonHeapMemoryUsage().getUsed());        return memStatus;    }     private Map<String, Object> getGcStatus() {        Map<String, Object> gcStatus = new HashMap<String, Object>();        if (!CollectionUtils.isEmpty(getGCMXBeanList())) {            // 不同的垃圾回收器            for (GarbageCollectorMXBean gcMXBean : getGCMXBeanList()) {                gcStatus.put(gcMXBean.getName() + "-" + Arrays.toString(gcMXBean.getMemoryPoolNames()) + "-count", gcMXBean.getCollectionCount());                gcStatus.put(gcMXBean.getName() + "-" + Arrays.toString(gcMXBean.getMemoryPoolNames()) + "-time", gcMXBean.getCollectionTime());            }        }        return gcStatus;    }     public void print() {        log.info("jvm mem status: {}", GsonUtils.toJson(getMemStatus()));         log.info("jvm gc status: {}", GsonUtils.toJson(getGcStatus()));    } }运行结果:2020-03-09 08:24:30,001 jvm mem status: {"Non-Heap mem Used":201168408,"Heap mem Used":710231248,"Heap mem Used Rato":"48%","Heap mem Committed":1467482112,"Non-Heap mem Init":2555904,"Non-Heap mem Committed":207945728,"Heap mem Init":1474297856,"Heap mem Max":1467482112}2020-03-09 08:24:30,001 jvm gc status: {"PS Scavenge-[PS Eden Space, PS Survivor Space]-count":210,"PS MarkSweep-[PS Eden Space, PS Survivor Space, PS Old Gen]-time":842,"PS MarkSweep-[PS Eden Space, PS Survivor Space, PS Old Gen]-count":4,"PS Scavenge-[PS Eden Space, PS Survivor Space]-time":2213}使用案例2      有业务功能大量并发,同时占用了大量数据库连接;从而导致连接池中可用连接长时间无空闲的话,就会导致业务持久化操作异常。下面就以使用Druid数据库连接池为例,通过Druid自带的MXbean获取连接池相关信息。PS:      1、多数Java连接池都会注册MXbean并记录重要监控数据,具体实现和数据线略有差别;      2、druid自带monitor;需要配置额外的数据库进行存储,可参见:https://github.com/alibaba/druid/wiki/druid-monitor%E8%AE%BE%E8%AE%A1      通过JMX连接JVM,可以看到DruidDataSource下有两个数据源,他们的ObjectName为:com.alibaba.druid:type=DruidDataSource,id=XXX,每个数据源的MXBean中各有一组Attribute记录相关监控项。实现代码:public class DruidMXBeanTest {     private static volatile MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();     private static void initMBeanServer() {        synchronized (DruidMXBeanTest.class) {            if (mBeanServer == null) {                try {                    mBeanServer = AccessController.doPrivileged(                            new PrivilegedExceptionAction<MBeanServer>() {                                @Override                                public MBeanServer run() throws DrsException {                                    return ManagementFactory.getPlatformMBeanServer();                                }                            });                } catch (Exception exp) {                    log.error("", exp);                }            }        }    }     private static MBeanServer getMBeanServer() {        if (mBeanServer == null) {            initMBeanServer();        }        return mBeanServer;    }     public static Set<ObjectInstance> queryMBeans(String objectName) {        try {            if (getMBeanServer() != null) {                return getMBeanServer().queryMBeans(new ObjectName(objectName), null);            }        } catch (Exception e) {            log.error("", e);        }        return null;    }     public static Map<String, Object> getAttributes(String objectName, List<String> attrNameList) {        try {            return getAttributes(new ObjectName(objectName), attrNameList);        } catch (Exception e) {            log.error("", e);        }        return null;    }     public static Map<String, Object> getAttributes(ObjectName objectName, List<String> attrNameList) {        Map<String, Object> result = new HashMap<String, Object>();        if (CollectionUtils.isEmpty(attrNameList) || getMBeanServer() == null) {            return result;        }        try {            AttributeList attributeList = getMBeanServer().getAttributes(objectName, attrNameList.toArray(new String[] {}));            for (int i = 0; i < attrNameList.size(); i++) {                result.put(attrNameList.get(i), i < attributeList.size() ? ((Attribute) attributeList.get(i)).getValue() : "");            }            return result;        } catch (Exception e) {            log.error("", e);        }        return result;    }     private Map<String, Map<String, Object>> getDruidDataSourceStatus() {        Map<String, Map<String, Object>> druidDataSourceMap = new HashMap<String, Map<String, Object>>();        try {            // 结果数量不固定并且ID每次随机,使用带通配符的ObjectName进行批量查询            Set<ObjectInstance> druidDataSourceSet = queryMBeans("com.alibaba.druid:type=DruidDataSource,id=*");            if (druidDataSourceSet != null) {                 // 一个数据源一组attribute                druidDataSourceSet.stream().forEach(objectInstance -> {                    Map<String, Object> druidDataSource = new HashMap<String, Object>();                     /**                     * 需要获取到的监控项                     *                      * druid更多相关监控项可以在com.alibaba.druid.pool.DruidDataSource中查看(注意使用时需要大写驼峰格式)                     */                    List<String> attrList = new ArrayList<String>() {                        {                            // 数据源用户名                            add("Username");                            // 最大连接池数量                            add("MaxActive");                            // 最小连接池数量                            add("MinIdle");                            // 连接池中空闲连接数                            add("PoolingCount");                            // 连接池中正在使用的连接数                            add("ActiveCount");                        }                    };                    druidDataSource = getAttributes(objectInstance.getObjectName(), attrList);                    druidDataSourceMap.put(String.valueOf(druidDataSource.get("Username")), druidDataSource);                });            }         } catch (Exception e) {            log.error("", e);        }        return druidDataSourceMap;    }     public void print() {        log.info("druid dataSource status: {}", GsonUtils.toJson(getDruidDataSourceStatus()));    }} 运行结果:2020-03-09 08:24:30,002 druid dataSource status: {"db1":{"Username":"db1","MaxActive":20,"MinIdle":5,"ActiveCount":0,"PoolingCount":5},"db2":{"Username":"db2","MaxActive":20,"MinIdle":10,"ActiveCount":1,"PoolingCount":9}}  总结很多开源中间件都会使用Management方式进行自定义扩展,记录相关运行状况数据;例如Kafka、logback等,都可以使用类似方式获取数据。参考资料:https://www.jianshu.com/p/5d854245051dhttps://baike.baidu.com/item/java.lang.management/5179868?fr=aladdinhttp://www.voidcn.com/article/p-cwctaeex-bsq.htmlhttps://zimt8.com/questions/16275207/https://docs.oracle.com/javase/tutorial/jmx/mbeans/mxbeans.htmlhttps://docs.oracle.com/javase/8/docs/api/java/lang/management/package-summary.html