zoukankan      html  css  js  c++  java
  • 记Introspector.getBeanInfo()引起的Full GC

    背景

      开发环境压力测试,100并发,FullGC频繁,一个转换工具类BeanWithMapHandlerUtil

        //把JavaBean转化为map
        public static Map<String, Object> beanToMap(Object bean) throws BaseAppException {
    
            Map<String, Object> map = new HashMap<>();
            //获取JavaBean的描述器  BeanUtils.describe() <String, String>
            try {
                BeanInfo b = Introspector.getBeanInfo(bean.getClass(), Object.class);
                //获取属性描述器
                PropertyDescriptor[] pds = b.getPropertyDescriptors();
                //对属性迭代
                for (PropertyDescriptor pd : pds) {
                    //属性名称
                    String propertyName = pd.getName();
                    //属性值,用getter方法获取
                    Method m = pd.getReadMethod();
                    //用对象执行getter方法获得属性值
                    Object properValue = m.invoke(bean);
                    // 判断是否有 list
                    if (properValue instanceof java.util.List) {
                        List list = (List) properValue;
    
                        if (CollectionUtils.isNotEmpty(list)) {
                            Class childClass = list.get(0).getClass();
    
                            if (isCommonDataType(childClass) || isWrapClass(childClass)) {
                                map.put(propertyName, list);
                            }
                            else {
                                List<Map<String, Object>> mapList = new ArrayList<Map<String, Object>>();
                                Object obj;
                                for (int i = 0; i < list.size(); i++) {
                                    obj = list.get(i);
                                    // list里是map或String,不会存在list里直接是list的
                                    Field[] fieldChilds = obj.getClass().getDeclaredFields();
                                    Map<String, Object> resultChild = new HashMap<String, Object>();
                                    for (Field field : fieldChilds) {
                                        // 重置属性可见(而且一般属性都是私有的),否则操作无效
                                        boolean accessible2 = field.isAccessible();
                                        if (!accessible2) {
                                            field.setAccessible(true);
                                        }
                                        // 获取属性名称及值存入Map
                                        String key = field.getName();
                                        Object objVal = field.get(obj);
                                        if (null != objVal && !"".equals(objVal)) {
                                            resultChild.put(key, field.get(obj));
                                        }
                                    }
                                    mapList.add(resultChild);
                                }
                                map.put(propertyName, mapList);
                            }
                        }
                    }
                    else {
                        //把属性名-属性值 存到Map中
                        if (null != properValue && !"".equals(properValue)) {
                            map.put(propertyName, properValue);
                        }
                    }
                }
            }
            catch (IllegalAccessException e) {
                throw  new BaseAppException("beanToMap IllegalAccessException {}", e);
            }
            catch (IntrospectionException e) {
                throw  new BaseAppException("beanToMap IntrospectionException {}", e);
            }
            catch (InvocationTargetException e) {
                throw  new BaseAppException("beanToMap InvocationTargetException {}", e);
            }
            return map;
        }

      通过thread -n 10 查看线程堆栈信息,线程处在阻塞状态

    原因

    "http-nio-8080-exec-44" Id=499 cpuUsage=3% BLOCKED on java.lang.Object@53793dcd owned by "http-nio-8080-exec-185" Id=640
        at java.lang.ClassLoader.loadClass(ClassLoader.java:404)
        -  blocked on java.lang.Object@53793dcd
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:348)
        at com.sun.beans.finder.ClassFinder.findClass(ClassFinder.java:103)
        at java.beans.Introspector.findCustomizerClass(Introspector.java:1301)
        at java.beans.Introspector.getTargetBeanDescriptor(Introspector.java:1295)
        at java.beans.Introspector.getBeanInfo(Introspector.java:425)
        at java.beans.Introspector.getBeanInfo(Introspector.java:262)
        at java.beans.Introspector.getBeanInfo(Introspector.java:224)
                BeanInfo b = Introspector.getBeanInfo(bean.getClass(), Object.class);

      可以看到java.lang.ClassLoader#loadClass(java.lang.String, boolean)中有一把synchronized锁,请求DTO转化成Map,并行变串行

    解决方案

    Map<String, Object> reqMa = JsonUtil.json2Map(JsonUtil.object2Json(bean));

    Object转Map换成JsonUtil工具类,验证如下:

    public static void main(String[] args) {
    
            RestInvocationDto req = new RestInvocationDto();
            req.setServer("test");
            req.setReqParams("test");
            req.setConfigServerKey("test");
            req.setBusinessUrl("test");
    
            Thread threadA = new Thread(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    Long begin = System.currentTimeMillis();
                    for (int i = 0; i < 1000; i++) {
                        Map<String, Object> reqMap = BeanWithMapHandlerUtil.beanToMap(req);
                    }
                    System.out.println("ThreadA costs " + (System.currentTimeMillis() - begin) + "ms");
                }
            });
    
            Thread threadB = new Thread(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    Long begin = System.currentTimeMillis();
                    for (int i = 0; i < 1000; i++) {
                        JsonUtil.json2Map(JsonUtil.object2Json(req));
                    }
                    System.out.println("ThreadB costs " + (System.currentTimeMillis() - begin) + "ms");
                }
            });
    
            threadA.start();
            threadB.start();
        }

    ThreadB costs 2321ms
    ThreadA costs 8312ms

     

    不积跬步,无以至千里;不积小流,无以成江海
  • 相关阅读:
    vim tab 和4个空格
    python 入门
    pyenv 以及 virtualenv
    Redis Cluster 理论知识
    使用Redis SETNX 命令实现分布式锁
    go runtime scheduler
    LeetCode Valid Parentheses
    LeetCode Rotate Image
    leetcode
    HDU 3657 Game(取数 最小割)经典
  • 原文地址:https://www.cnblogs.com/hzzjj/p/15342964.html
Copyright © 2011-2022 走看看