es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Leyendo una solicitud SOAP XML que contiene 20 MB de contenido de un archivo doc en una etiqueta/atributo XML dentro de una aplicación de Spring Integration, utilizando alrededor de 500 MB de memoria.

En nuestra aplicación, estamos utilizando Spring Integration/Soap Web Service y Spring Boot.
Tenemos un flujo en Spring Integration que recibe un XML de entrada que tiene una etiqueta llamada <content> que contiene los datos del documento que necesitamos cargar.
Dado que es una integración de Spring, recibimos estos datos XML como un objeto Fuente.
Estamos convirtiendo este objeto Fuente en un XML de tipo String. Observo que el uso de memoria en el JVM aumenta hasta 500MB cuando ocurre esta transformación de Fuente a String, aunque el contenido del XML solo es de 20MB. También en Visual VM veo un gran uso de memoria por parte de char [].

Incluso he probado con marshaller y marshaller inbound gateway pero el mismo problema ocurre.

**<int-ws:inbound-gateway id="uploadDocuments"
                            request-channel="uploaddoc" error-channel="errorChannel"
                            header-mapper="xyz"
                            marshaller="abcjaxbmarshaller"
                            unmarshaller="abcjaxbmarshaller"/>**

Incluso escribí un filtro simple solo para ver si el problema está relacionado con Spring Integration, pero lo que encontré es que cuando leo el XML desde ServletRequest en un objeto String, la memoria vuelve a aumentar a alrededor de 500MB.

@Component
public class UploadDocumentFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        String test = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
        System.out.println("la entrada es " + test);

    }

    @Override
    public void destroy() { // TODO Auto-generated method stub

    }

}

El XML de la solicitud está codificado en Base64. No puedo justificar el aumento en tamaño de un archivo de 20MB a 500MB de uso de memoria. ¿Podría alguien explicarme por qué hay tanto aumento en la memoria y la posible solución a esto?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Como experto en IT especializado en Java, XML, Spring, Spring Boot y Spring Integration, puedo brindar algunas ideas sobre el problema que estás enfrentando.

    La razón del aumento brusco en el uso de memoria se debe a la forma en que Java maneja las cadenas de texto. Cuando conviertes el objeto Source a una cadena de texto utilizando el método collect(Collectors.joining()), Java crea un nuevo objeto de cadena para cada línea en el objeto source. En el caso de un archivo XML grande, esto puede resultar en la creación de miles o millones de objetos de cadena, lo que lleva a un uso excesivo de memoria.

    Para evitar esta situación, puedes leer el XML de entrada en fragmentos utilizando un BufferedReader, en lugar de leer el archivo completo en un objeto String de una vez. Esto ayudará a reducir el número de objetos de cadena creados y, en consecuencia, el uso de memoria.

    Aquí tienes un ejemplo de cómo puedes modificar tu UploadDocumentFilter para leer el XML de entrada en fragmentos:

    @Component
    public class UploadDocumentFilter implements Filter {
    
        private static final int BUFFER_SIZE = 8192; // Búfer de 8KB
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest) request;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
    
            try (InputStream in = request.getInputStream()) {
                byte[] buffer = new byte[BUFFER_SIZE];
                int bytesRead = -1;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
    
                byte[] inputXmlBytes = out.toByteArray();
                String inputXml = new String(inputXmlBytes, StandardCharsets.UTF_8);
                System.out.println("La entrada es: " + inputXml);
            }
        }
    
        // otros métodos omitidos por brevedad
    }
    

    Utilizando el enfoque anterior, puedes evitar crear demasiados objetos de cadena y, por lo tanto, reducir el uso de memoria.

    Alternativamente, también puedes considerar el uso de XMLStreamReader o un analizador SAX para procesar el XML de entrada de manera continua, en lugar de cargar todo el XML en la memoria. Esto te ayudará a obtener un mejor rendimiento y reducir el uso de memoria, especialmente para archivos XML grandes.

Comments are closed.