当前位置:  编程技术>软件工程/软件设计
本页文章导读:
    ▪Spring攻略学习笔记(2.11)------创建Bean后处理器      一、知识点          Bean后处理器允许在初始化回调方法前后进行额外的Bean处理。Bean后处理器的主要特性是逐个处理IoC容器中的所有Bean实例,而不只是单个Bean实例。一.........
    ▪内核中accept连接时创建socket结构错误导致的内存泄露        强烈推荐一下淘宝褚霸的这篇文章: http://blog.yufeng.info/archives/2456   注:这里我们只关心TCP套接字,所以文章中说sock结构或者socket结构的时候都只针对TCP协议。     在测试内核.........
    ▪release下优雅的crash      C++里面有使用异常处理的机制,但是那些需要throw catch捕捉等操作 而通常一个win程序,我们会因为各种原因导致其crash,而不一定是throw 了某个exception。我们需要这样一个机制,可以在.........

[1]Spring攻略学习笔记(2.11)------创建Bean后处理器
    来源: 互联网  发布时间: 2013-11-19

一、知识点

         Bean后处理器允许在初始化回调方法前后进行额外的Bean处理。Bean后处理器的主要特性是逐个处理IoC容器中的所有Bean实例,而不只是单个Bean实例。一般来说,Bean后处理器用于检查Bean属性的有效性,或根据特定条件修改Bean属性。

        Bean后处理器的基本要求是实现BeanPostProcessor接口。通过实现postProcessBeforeInitialization() 和 postProcessAfterInitialization() 方法,可以在初始化回调方法前后处理所有Bean。Spring将在调用初始化回调方法前后向这两个方法传递每个Bean实例,步骤如下:

        (1)构造函数或者工厂方法创建Bean实例

        (2)为Bean属性设值和Bean引用

        (3)调用感知接口中定义的设值方法

        (4)将Bean实例传递给每个Bean后处理器中的postProcessBeforeInitialization()方法

        (5)调用初始化回调方法

        (6)将Bean实例传递给每个Bean后处理器中的postProcessAfterInitialization()方法

        (7)Bean准备就绪,可以使用

        (8)容器关闭时,调用销毁回调函数

        使用Bean工厂作为IoC容器时,Bean后处理器只能编程注册,即通过addBeanPostProcessor方法注册。如果使用应用程序上下文,只需要在Bean配置文件中声明一个处理器实例,它就会自动注册。


二、代码示例

         StorageConfig标记接口  

package com.codeproject.jackie.springrecipesnote.springadvancedioc;

/**
 *  标记接口,让Bean后处理器区分应检查的Bean
 * @author jackie
 *
 */
public interface StorageConfig {
	public String getPath();
}
  Cashier类
package com.codeproject.jackie.springrecipesnote.springadvancedioc;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;

import org.springframework.beans.factory.BeanNameAware;

/**
 * Cashier类实现BeanNameAware感知接口和,StorageConfig标记接口
 * @author jackie
 *
 */
public class Cashier implements BeanNameAware, StorageConfig {
	private String name;  
    private BufferedWriter writer;
	private String path;  
      
    public void setPath(String path) {
		this.path = path;
	}

	public void openFile() throws IOException {
		File file = new File(path, name + ".txt");
        FileWriter fw = new FileWriter(file, true);  
        writer = new BufferedWriter(fw);  
    }  
      
    public void checkout(ShoppingCart cart) throws IOException {  
        double total = 0;  
        for (Product product : cart.getItems()) {  
            total += product.getPrice();  
        }  
        writer.write(new Date() + "\t" + total + "\r\n");  
        writer.flush();  
    }  
    
    public void closeFile() throws IOException {  
        writer.close();  
    }

	@Override
	public String getPath() {
		return path;
	}

	@Override
	public void setBeanName(String beanName) {
		this.name = beanName;
	}  
}
PathCheckingBeanPostProcessor自定义Bean后处理器

package com.codeproject.jackie.springrecipesnote.springadvancedioc;

import java.io.File;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 *  通用的组件,确保文件在打开之前存在
 * @author jackie
 *
 */
public class PathCheckingBeanPostProcessor implements BeanPostProcessor {
    /**
     *  在文件打开之前进行路径检查
     *  须返回所处理的Bean的一个实例。
     */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
                // 如果Bean实现了StorageConfig接口,检查路径是否存在
                if (bean instanceof StorageConfig) {
			String path = ((StorageConfig)bean).getPath();
			File file = new File(path);
			if (!file.exists()) {
				file.mkdirs();
			}
		}
		return bean;
	}

	/**
	 * 即使什么都不做,也必须返回原来的Bean实例
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}
}
Bean配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">   
    <!-- 注册Bean后处理器 -->
    <bean class="com.codeproject.jackie.springrecipesnote.springadvancedioc.PathCheckingBeanPostProcessor" /> 
    
    <bean id="aaa" class="com.codeproject.jackie.springrecipesnote.springadvancedioc.Battery">
    	<property name="name" value="AAA" />
    	<property name="price" value="2.5" />
    </bean>
    
    <bean id="cdrw" class="com.codeproject.jackie.springrecipesnote.springadvancedioc.Battery">
    	<property name="name" value="CD-RW" />
    	<property name="price" value="1.5" />
    </bean>
    
    <bean id="dvdrw" class="com.codeproject.jackie.springrecipesnote.springadvancedioc.Battery">
    	<property name="name" value="DVD-RW" />
    	<property name="price" value="3.0" />
    </bean>
    
    <bean id="shoppingCart" class="com.codeproject.jackie.springrecipesnote.springadvancedioc.ShoppingCart" scope="prototype" />
    
    <bean id="cashier1" class="com.codeproject.jackie.springrecipesnote.springadvancedioc.Cashier" init-method="openFile" destroy-method="closeFile" >
    	<property name="path" value="c:/cashier1" />
    </bean> 
</beans>
测试类

package com.codeproject.jackie.springrecipesnote.springadvancedioc;

import java.io.IOException;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author jackie
 *
 */
public class BeanPostProcessorTest {
	@Test
	public void testBeanPostProcessor() throws IOException {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		Product aaa = (Product) applicationContext.getBean("aaa");
		Product cdrw = (Product) applicationContext.getBean("cdrw");
		Product dvdrw = (Product) applicationContext.getBean("dvdrw");
		
		ShoppingCart shoppingCart = (ShoppingCart) applicationContext.getBean("shoppingCart");
		shoppingCart.addItem(aaa);
		shoppingCart.addItem(cdrw);
		shoppingCart.addItem(dvdrw);
		
	    Cashier cashier = (Cashier) applicationContext.getBean("cashier1");
	    cashier.checkout(shoppingCart);
	}
}

        注:书上说如果使用JSR-250注解@PostConstruct和@PreDestroy,并且一个CommonAnnotationBeanPostProcessor实例调用初始化方法,那么PathCheckingBeanPostProcessor不能正常工作,因为它的默认优先级低于CommonAnnotationBeanPostProcessor。经我实践证明,PathCheckingBeanPostProcessor仍然可以正常工作。

作者:jackiehff 发表于2013-4-26 15:25:05 原文链接
阅读:55 评论:0 查看评论

    
[2]内核中accept连接时创建socket结构错误导致的内存泄露
    来源: 互联网  发布时间: 2013-11-19
  强烈推荐一下淘宝褚霸的这篇文章: http://blog.yufeng.info/archives/2456
  注:这里我们只关心TCP套接字,所以文章中说sock结构或者socket结构的时候都只针对TCP协议。
 
  在测试内核模块时,内核会因为内存耗尽而panic,使用crash工具查看core文件,提示的信息是"Kernel panic - not syncing: Out of memory and no killable processes..."。也就是说内核没有内存可以使用,并且也没有进程可以杀死来释放内存。不用说,肯定是发生了内存泄露!写这篇文章是为了总结一下查找这个BUG的过程,反思一下。在这个过程中用到了很多工具和方法,写下来对遇到类似问题的人或许有些帮助。
  内核中运行的程序主要是我的内核模块和应用层的HTTP服务器。所以造成内核泄露要么是内核模块,要么是HTTP服务器。
  问题出在HTTP服务器的可能性不大,首先这个服务器已经上线了挺长时间,相对比较稳定;其次如果是应用进程占用的内存过多,内核完全会杀死这个用户进程来释放内存,也就不会出现“Out of memory and no killable processes”这样的信息。所以基本可以肯定问题出在内核模块上。为了保险,还是要看一下应用层的服务器进程占用了多少内存。其中一台服务器的内存比较小,所以在这个机器上问题很快就可以重现。在内存消耗量比较大(通过free -m可以看到)时,通过top命令查看应用服务器进程占用的RSS内存(也可以通过/proc/[pid]/statem文件查看),应用服务器占用的内存只有100多M。接着查看进程占用的文件数,也就是连接数,这个信息可以通过/proc/[pid]/fd目录获取。应用服务器占用的连接数也非常少。通过查看应用服务器实际占用的内存数和连接数,现在就要开始去内核模块中查找问题了。

  我的内核模块在sysfs中注册了相应的项,通过这些可以看到当前占用的内存数量。这些项的统计信息都是在分配内存成功、释放之后更新的,可以实时反映当前的内存占用信息。为了方便,我编写了一个脚本,每隔一秒就把这些信息刷到终端上,如下图所示:


  total_mem项的单位是KB,所以这里看到占用的内存大约为3M。内存统计的地方都已经反复地检查过了,所以这个量是可信的。这个结果不是我想看到的,如果统计出来的量大的话,我就会从内核模块分配和释放的地方去找问题。但是现在这个量这么小,很明显不是内核模块中调用kmalloc()或kmem_cache_alloc()(内核模块调用)分配的内存没有被释放。这个时候很容易陷入迷茫,估计很多人都会去反复地检查自己的代码,看看是否有内存没有释放的地方。如果我最开始也这样做的话,或许也能早点发现BUG,但是也有可能要花更长的时间。一般情况下,代码中有很多不同功能的模块,每个模块的功能复杂度不同。你去检查代码往往会忽略功能简单的模块,即使是在遇到BUG的时候。而且这样盲目地去遍历各个可能出问题的地方,也很不明智,因为你现在了解到的信息还太少。所以,这个时候,首先要做的不是去检查代码,而是首先要先通过各种工具和手段,来看一看内存究竟用到了什么地方。你现在遇到的问题,很多前辈们也遇到过。为了方便快捷地解决遇到的问题,前辈们已经为我们开发出了很多很多方便好用的工具,为什么不拿来用呢?貌似扯的有点远了,继续我们的问题.......
  很庆幸看到了霸爷的《Linux Used内存到底哪里去了?》这篇文章,所在我决定先按照这篇文章的方法,找到系统被使用的内存都在哪里。正如文章中所说的,内存主要用3个去向:进程消耗、slab消耗和pagetable消耗。其中pagetable消耗是内核管理页面时的消耗,也就是struct page等结构的消耗。slab消耗不仅包括管理slab结构本身的消耗,还包括每个slab缓存的内存。slab缓存的内存在这三类中占用的量也是很大的。我们知道内核模块或驱动、内核活动本身分配内存,都是基于slab的。kmalloc()分配的内存也是从slab中获取的。按照霸爷的方法,我计算除了进程占用的内存、slab消耗的内存和pagetable占用的内存,这个值和free命令看到的基本吻合。这两个值会有偏差,因为会对共享库占用的内存重复计算。只要差别不大,就可以。我为什么要按照文章中的方法自己做了一次呢?很简单,首先我可以熟悉一些工具的使用,了解这些工具可以提供给我什么信息;其次,在这个过程中我可以知道怎么从/proc目录下找到一些内存相关的项,通过这些项可以看到系统的运行情况。
  OK,现在计算了系统使用的内存量,并且和free看到的一样。但是这个时候我急于解决问题,心浮气躁,犯了一个很大的错误。我已经计算了进程消耗的内存量、slab消耗的内存量、pagetable消耗的内存量,这时我至少应该看一看这三个值哪个比较大吧?看看内存究竟在哪个地方消耗的多吧,可是我竟然没有!深深地鄙视一下自己!如果这时我只要留心看一下,我就会发现slab占用的内存非常大(后话,情况确实如此),这可以大大缩短我消耗的时间。如果发现slab占用的内存太大,可以用slabtop这个工具来看看哪个slab占用的内存过多(不同的slab有不同的名称)。slabtop显示的信息是根据/proc/slabinfo这个文件生成的。很幸运,虽然我犯了一个错误,可我还是使用了slabtop这个工具看查看slab消耗的内存情况(现在也不知道当时为什么要这样做)。当然这个时候我看这个工具的输出完全没有目的性,胡乱的看,第一次用总是很茫然。在看的过程中,我注意到了一个叫”TCP“的Slab的缓存大小一直在增加,而且增加的速度也挺快的。这时我意识到问题可能出现在这个Slab上,果断重启机器重新构造环境,并且单独打开了一个终端,在这个终端运行slabtop,这样在内核panic之后,你在终端(Xshell)仍然可以看到最近时间的slabtop输出。程序启动之后,我就开始在内核源码中找”TCP“对应哪个slab。首先从名称来看,这个肯定和TCP协议相关。看过内核协议栈的肯定都知道,当分配套接字的时候,会从tcp_prot变量的slab成员中分配sock结构,我首先找这个slab的名称是什么。tcp_prot变量的slab成员在定义的时候是没有初始化的,它是在注册到系统的时候才初始化,注册是在inet_init()函数中进行的,如下所示:

static int __init inet_init(void)
{
    ......

    rc = proto_register(&tcp_prot, 1);
    if (rc)
        goto out;
    ......
}
  而slab的创建是在prot_register()函数中,如下所示:

int proto_register(struct proto *prot, int alloc_slab)
{
    if (alloc_slab) {
        prot->slab = kmem_cache_create(prot->name, prot->obj_size, 0,
                    SLAB_HWCACHE_ALIGN | prot->slab_flags,
                    NULL);


        if (prot->slab == NULL) {
            printk(KERN_CRIT "%s: Can't create sock SLAB cache!\n",
                   prot->name);
            goto out;
        }
    ......
    }
    ......
}
结合上面两部分代码,可以看到这里创建的slab的名称是tcp_prot的name成员,那这个name成员是什么呢?看看tcp_prot的定义就知道了,如下所示:

struct proto tcp_prot = {
    .name            = "TCP",
    .owner            = THIS_MODULE,
    ......
};
 没错,就是看到的那个"TCP"slab就是tcp_prot的slab成员对应的那个。tcp_prot的slab是在inet_create()中创建sock结构的时候用到。此时"TCP"slab占用的内存过多,是因为有太多的sock结构没有释放导致的。
  我的内核模块是用来做TCP连接迁移,会在两个地方创建sock结构,一个是在内核中创建新的连接时(和传统的方式不一样),一个是在发送迁移信息的时候(正常的TCP连接)。因为构建的连接在成功之后放到系统中了,后期的释放完全按照TCP协议的正常关闭流程,不由内核模块控制了,如果是这造成的,不好确定。所以先确定是否是因为发送迁移信息时创建的连接没有关闭导致的。好好检查了一番代码,基本排除了这种可能。下面就是我也确定是否是因为我创建的连接没有释放导致的,最先想到的就是在sk_free()处添加钩子(也就是kprobe机制)。当然,有Systemtap这个强大的脚本工具,自然不会再编写C代码,然后用insmod或modprobe插入模块的方式了。下面是我的sk_free.stp脚本:
%{
#include <linux/tcp.h>
#include <net/tcp.h>
#include <linux/sched.h>
%}

function sk_free_test:long(arg1:long) %{
    struct sock *sk = (struct sock *)THIS->arg1;

    if (inet_sk(sk)->sport == htons(80)) {
        _stp_printf("func:sk_free_test, sk=%p, sk->sk_state=%u, sk->sk_wmem_alloc=%d,slab:%p,daddr=%d.%d.%d.%d\n",
                sk, sk->sk_state, atomic_read(&sk->sk_wmem_alloc), sk->sk_prot_creator->sl      
    
[3]release下优雅的crash
    来源: 互联网  发布时间: 2013-11-19

C++里面有使用异常处理的机制,但是那些需要throw catch捕捉等操作


而通常一个win程序,我们会因为各种原因导致其crash,而不一定是throw 了某个exception。我们需要这样一个机制,可以在程序因为各种原因crash时掌握程序的情况,并能够做点事情,这就是所谓的“优雅的结束”。win api恰为我们提供了这样一个函数SetUnhandledExceptionFilter。

win程序在release情况下,发生了异常,并且没有被捕捉处理,就会调用一个默认的UnhandledExceptionFilter函数,这个函数还传入一个参数_EXCEPTION_POINTERS*,里面有crash的一些信息。


如果我们想介入这个挂掉的过程,比如优雅的结束,比如收集crash信息并上传,然后告诉用户你挂了。。那就可以自定一个函数如

 long __stdcall MyUnhandledExceptionFilter (_EXCEPTION_POINTERS* ExceptionInfo){。。。

}

然后使用SetUnhandledExceptionFilter set进来。

MyUnhandledExceptionFilter 函数有三种返回值:

EXCEPTION_EXECUTE_HANDLER equ 1 表示我已经处理了异常,可以优雅地结束了
EXCEPTION_CONTINUE_SEARCH equ 0 表示我不处理,其他人来吧,于是windows调用默认的处理程序显示一个错误框,并结束
EXCEPTION_CONTINUE_EXECUTION equ -1 表示错误已经被修复,请从异常发生处继续执行  



注意这些只在release下work


作者:leonwei 发表于2013-4-27 9:53:07 原文链接
阅读:3 评论:0 查看评论

    
最新技术文章:
▪主-主数据库系统架构    ▪java.lang.UnsupportedClassVersionError: Bad version number i...    ▪eclipse项目出现红色叉叉解决方案
▪Play!framework 项目部署到Tomcat    ▪dedecms如何做中英文网站?    ▪Spring Batch Framework– introduction chapter(上)
▪第三章 AOP 基于@AspectJ的AOP    ▪基于插件的服务集成方式    ▪Online Coding开发模式 (通过在线配置实现一个表...
▪观察者模式(Observer)    ▪工厂模式 - 程序实现(java)    ▪几种web并行化编程实现
▪机器学习理论与实战(二)决策树    ▪Hibernate(四)——全面解析一对多关联映射    ▪我所理解的设计模式(C++实现)——解释器模...
▪利用规则引擎打造轻量级的面向服务编程模式...    ▪google blink的设计计划: Out-of-Progress iframes    ▪FS SIP呼叫的消息线程和状态机线程
▪XML FREESWITCH APPLICATION 实现    ▪Drupal 实战    ▪Blink: Chromium的新渲染引擎
▪(十四)桥接模式详解(都市异能版)    ▪你不知道的Eclipse用法:使用Allocation tracker跟...    ▪Linux内核-进程
▪你不知道的Eclipse用法:使用Metrics 测量复杂度    ▪IT行业为什么没有进度    ▪Exchange Server 2010/2013三种不同的故障转移
▪第二章 IoC Spring自动扫描和管理Bean    ▪CMMI简介    ▪目标检测(Object Detection)原理与实现(六)
▪值班总结(1)——探讨sql语句的执行机制    ▪第二章 IoC Annotation注入    ▪CentOS 6.4下安装Vagrant
▪Java NIO框架Netty1简单发送接受    ▪漫画研发之八:会吃的孩子有奶吃    ▪比较ASP和ASP.NET
▪SPRING中的CONTEXTLOADERLISTENER    ▪在Nginx下对网站进行密码保护    ▪Hibernate从入门到精通(五)一对一单向关联映...
▪.NET领域驱动设计—初尝(三:穿过迷雾走向光...    ▪linux下的块设备驱动(一)    ▪Modem项目工作总结
▪工作流--JBPM简介及开发环境搭建    ▪工作流--JBPM核心服务及表结构    ▪Eclipse:使用JDepend 进行依赖项检查
▪windows下用putty上传文件到远程Linux方法    ▪iBatis和Hibernate的5点区别    ▪基于学习的Indexing算法
▪设计模式11---设计模式之中介者模式(Mediator...    ▪带你走进EJB--JMS编程模型    ▪从抽象谈起(二):观察者模式与回调
▪设计模式09---设计模式之生成器模式(Builder)也...    ▪svn_resin_持续优化中    ▪Bitmap recycle方法与制作Bitmap的内存缓存
▪Hibernate从入门到精通(四)基本映射    ▪设计模式10---设计模式之原型模式(Prototype)    ▪Dreamer 3.0 支持json、xml、文件上传
▪Eclipse:使用PMD预先检测错误    ▪Jspx.net Framework 5.1 发布    ▪从抽象谈起(一):工厂模式与策略模式
▪Eclipse:使用CheckStyle实施编码标准    ▪【论文阅读】《Chain Replication for Supporting High T...    ▪Struts2 Path_路径问题
▪spring 配置文件详解    ▪Struts2第一个工程helloStruts极其基本配置    ▪Python学习入门基础教程(learning Python)--2 Python简...
▪maven springmvc环境配置    ▪基于SCRUM的金融软件开发项目    ▪software quality assurance 常见问题收录
▪Redis集群明细文档    ▪Dreamer 框架 比Struts2 更加灵活    ▪Maven POM入门
▪git 分支篇-----不断更新中    ▪Oracle非主键自增长    ▪php设计模式——UML类图
▪Matlab,Visio等生成的图片的字体嵌入问题解决...    ▪用Darwin和live555实现的直播框架    ▪学习ORM框架—hibernate(二):由hibernate接口谈...
▪(十)装饰器模式详解(与IO不解的情缘)    ▪无锁编程:最简单例子    ▪【虚拟化实战】网络设计之四Teaming
▪OSGi:生命周期层    ▪Javascript/Jquery——简单定时器    ▪java代码 发送GET、POST请求
▪Entity Framework底层操作封装(3)    ▪HttpClient 发送GET、POST请求    ▪使用spring框架,应用启动时,加载数据
▪Linux下Apache网站目录读写权限的设置    ▪单键模式的C++描述    ▪学习ORM框架—hibernate(一):初识hibernate
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3