10

My JVM keeps on crashing continuously and unexpectedly at libzip.so all the time. I've filed the bug with Oracle, but decided to see if anyone here has experienced the problem and if so, how did you deal with it? This is a web app running off

Linux 2.6.34-gentoo-r6 #1 SMP Fri Sep 24 00:15:06 EDT 2010 i686 Intel(R) Xeon(R) CPU X5460 @ 3.16GHz GenuineIntel GNU/Linux
Tomcat 7.0.14 with jsvc.

I've included a snapshot of the bug report below. Its a standalone server, nobody is accessing any of tomcat's jar or any other jars while running and its not hosted from NFS.

 SIGSEGV (0xb) at pc=0xb6a72295, pid=19470, tid=241171312

 JRE version: 6.0_29-b11  Java VM: Java HotSpot(TM) Server VM (20.4-b02 mixed mode linux-x86 )

 Problematic frame:  C  [libzip.so+0x5295]  double+0x45

 If you would like to submit a bug report, please visit:    http://java.sun.com/webapps/bugreport/crash.jsp  The crash happened outside the Java Virtual Machine in native code.  See problematic frame for where to report the bug.


---------------  T H R E A D  ---------------

Current thread (0x1044dc00):  JavaThread "catalina-exec-177" daemon [_thread_in_native, id=21423, stack(0x0e5af000,0x0e600000)]

siginfo:si_signo=SIGSEGV: si_errno=0, si_code=2 (SEGV_ACCERR), si_addr=0x10dfc000

Registers: EAX=0x10d00018, EBX=0xb6a7dabc, ECX=0x000fbfe6, EDX=0x00000000 ESP=0x0e5febf0, EBP=0x0e5fec18, ESI=0x0eadc098, EDI=0x10458800 EIP=0xb6a72295, EFLAGS=0x00010246, CR2=0x10dfc000

Top of Stack: (sp=0x0e5febf0) 0x0e5febf0:   b7869118 00000000 0ef2e648 b6a71216 0x0e5fec00:   13d1f690 10c492b0 0000bfe1 b6a7dabc 0x0e5fec10: 10458800 0ef2e648 0e5fec48 b6a7134d 0x0e5fec20:   10458800 00000004 0e5fec48 b77b727c 0x0e5fec30:   00000007 3d4af570 ffffffff b6a7dabc 0x0e5fec40:   31fe9ad0 1044dd20 0e5feca8 b6a6f585 0x0e5fec50:   0ef2e648 00000004 00000002 00000000 0x0e5fec60:   10b59810 3d5cff58 00000002 00004114

Instructions: (pc=0xb6a72295) 0xb6a72275:   05 01 00 00 0f 86 79 03 00 00 83 fa 02 76 41 8b 0xb6a72285:   57 40 8b 4f 50 8b 47 30 8b 77 38 d3 e2 8b 4f 64 0xb6a72295:   0f b6 44 01 02 31 c2 8b 47 4c 21 c2 8b 47 2c 89 0xb6a722a5:   57 40 21 c1 8b 47 3c 0f b7 04 50 89 45 f0 66 89

Register to memory mapping:

EAX=0x10d00018 is an unknown value EBX=0xb6a7dabc: <offset 0x10abc> in /usr/local/jdk1.6.0_29/jre/lib/i386/libzip.so at 0xb6a6d000 ECX=0x000fbfe6 is an unknown value EDX=0x00000000 is an unknown value ESP=0x0e5febf0 is pointing into the stack for thread: 0x1044dc00 EBP=0x0e5fec18 is pointing into the stack for thread: 0x1044dc00 ESI=0x0eadc098 is an unknown value EDI=0x10458800 is an unknown value


Stack: [0x0e5af000,0x0e600000],  sp=0x0e5febf0,  free space=318k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) C  [libzip.so+0x5295]  double+0x45 C  [libzip.so+0x434d]  double+0x10d C  [libzip.so+0x2585]  Java_java_util_zip_Deflater_deflateBytes+0x355 J  java.util.zip.Deflater.deflateBytes(J[BII)I

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) J  java.util.zip.Deflater.deflateBytes(J[BII)I J  java.util.zip.GZIPOutputStream.finish()V J  org.apache.coyote.http11.Http11NioProcessor.actionInternal(Lorg/apache/coyote/ActionCode;Ljava/lang/Object;)V J  org.apache.coyote.http11.AbstractHttp11Processor.action(Lorg/apache/coyote/ActionCode;Ljava/lang/Object;)V J  org.apache.catalina.connector.Response.finishResponse()V J  org.apache.catalina.connector.CoyoteAdapter.service(Lorg/apache/coyote/Request;Lorg/apache/coyote/Response;)V J  org.apache.coyote.http11.Http11NioProcessor.process(Lorg/apache/tomcat/util/net/NioChannel;)Lorg/apache/tomcat/util/net/AbstractEndpoint$Handler$SocketState; J  org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run()V J  java.util.concurrent.ThreadPoolExecutor$Worker.run()V j  java.lang.Thread.run()V+11 v  ~StubRoutines::call_stub

Any hints appreciated.

Marek Sebera
  • 39,650
  • 37
  • 158
  • 244
Daniil
  • 1,398
  • 2
  • 17
  • 28
  • 1
    I haven't seen that particular problem, but I can say that I've seen a LOT of problems with Java 6u29 at work. People that need to have 6 ended up getting back dated to the previous update, and those that aren't required to have 6 have gone to 7u1 (which not only doesn't seem to have any problems in our environment, but is noticeably faster than 6). – Brian Knoblauch Dec 06 '11 at 19:32
  • does this happen when you try running some application? – Marek Sebera Dec 06 '11 at 19:33
  • Your best option is to downgrade to stable JRE version. – Victor Sorokin Dec 06 '11 at 19:33
  • Actually, I keep on upgrading because I was getting this problem since version 5. It has slowed down in frequency with each upgrade. Even up to today its been working without a problem for a week and then, bam, crashed 3 times already. I'm a bit cautious to upgrade to 7 as I need to evaluate how the app will run with the latest Java version, so its not as speedy. – Daniil Dec 06 '11 at 19:38
  • disable gzip compression, tomcat uses semi hack to do the flush instead of rolling a pure java zip (i have modified code to utilize jzlib) – bestsss Dec 06 '11 at 19:59
  • another thing to check would be to see if you're trying to use a 64-bit library on a 32-bit machine, or vice versa. – icfantv Dec 06 '11 at 20:01
  • @icfantv, java would have not started at all, if mismatched – bestsss Dec 06 '11 at 20:19
  • @bestsss, only if the library required a reference at JVM startup no? – icfantv Dec 06 '11 at 20:50
  • @icfantv, but it does, it has to decompress all the jars in the bootstrap, it's a super-duper core lib – bestsss Dec 06 '11 at 20:53
  • @bestsss - do you have a patch for tomcat 7 by any chance? I'll do an upgrade to JDK7 to see if the problem persists. – Daniil Dec 06 '11 at 21:37
  • Is this even Java related problem? Shouldn't I look into libzip.so instead? Any thoughts... – Daniil Dec 06 '11 at 21:38
  • @Daniil, there is no significant difference between tomcat 6/7 in the coyote adapter except I did write the code for NIO connector (basically running tomcat6 coyote NIO w/ catalina 5.5). I will check source of tomcat7 and post `org.apache.coyote.http11.filters.GzipOutputFilter` – bestsss Dec 06 '11 at 22:07
  • 1
    Here's a crazy suggestion, try memtest86 and see if you have bad RAM. http://www.memtest.org/ – BillRobertson42 Dec 07 '11 at 04:48
  • @Bill, xeon's usually run on ECC, so that would be unlikely. – bestsss Dec 07 '11 at 07:44
  • @Bill, its a good suggestion. We, however, always memtest machines before production and at this point the procedure cannot be done that quickly. – Daniil Dec 07 '11 at 18:22
  • 2
    Well, upgrading to JDK 7 didn't help. Crashed in a different place: C [libzip.so+0x8324] deflate_slow+0x44 and siginfo:si_signo=SIGSEGV: si_errno=0, si_code=1 (SEGV_MAPERR), si_addr=0x12400000 – Daniil Dec 07 '11 at 20:11

1 Answers1

2

This is the patched class, sort of nightly built and need to test it. The JGZipOutputStream class can be found here: Creating gzip file using jzlib

JZlib can be found at http://www.jcraft.com/jzlib/

You can patch it either by adding a small jar in the bootstrap path ( -Xbootclasspath/a: ) or unzip/jar the tomcat itself.

Extra bonus is that java impl. is actually faster than the native code.

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.coyote.http11.filters;

import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.coyote.OutputBuffer;
import org.apache.coyote.Response;
import org.apache.coyote.http11.OutputFilter;
import org.apache.tomcat.util.buf.ByteChunk;

import bestsss.util.JGZipOutputStream;

import com.jcraft.jzlib.JZlib;

/**
 * Gzip output filter.
 * 
 * @author Remy Maucherat
 */
public class GzipOutputFilter implements OutputFilter {


    /**
     * Logger.
     */
    protected static org.apache.juli.logging.Log log =
        org.apache.juli.logging.LogFactory.getLog(GzipOutputFilter.class);


    // ----------------------------------------------------- Instance Variables


    /**
     * Next buffer in the pipeline.
     */
    protected OutputBuffer buffer;


    /**
     * Compression output stream.
     */
    protected OutputStream compressionStream = null;


    /**
     * Fake internal output stream.
     */
    protected OutputStream fakeOutputStream = new FakeOutputStream();


    // --------------------------------------------------- OutputBuffer Methods


    /**
     * Write some bytes.
     * 
     * @return number of bytes written by the filter
     */
    @Override
    public int doWrite(ByteChunk chunk, Response res)
        throws IOException {
        if (compressionStream == null) {
            compressionStream = new JGZipOutputStream(fakeOutputStream, new byte[Math.min(32768, Math.max(2048, Integer.highestOneBit(chunk.getLength()-1)<<1))]);
        }
        compressionStream.write(chunk.getBytes(), chunk.getStart(), 
                                chunk.getLength());
        return chunk.getLength();
    }


    @Override
    public long getBytesWritten() {
        return buffer.getBytesWritten();
    }


    // --------------------------------------------------- OutputFilter Methods

    /**
     * Added to allow flushing to happen for the gzip'ed outputstream
     */
    public void flush() {
        if (compressionStream != null) {
            try {
                if (log.isDebugEnabled()) {
                    log.debug("Flushing the compression stream!");
                }
                compressionStream.flush();
            } catch (IOException e) {
                if (log.isDebugEnabled()) {
                    log.debug("Ignored exception while flushing gzip filter", e);
                }
            }
        }
    }

    /**
     * Some filters need additional parameters from the response. All the 
     * necessary reading can occur in that method, as this method is called
     * after the response header processing is complete.
     */
    @Override
    public void setResponse(Response response) {
        // NOOP: No need for parameters from response in this filter
    }


    /**
     * Set the next buffer in the filter pipeline.
     */
    @Override
    public void setBuffer(OutputBuffer buffer) {
        this.buffer = buffer;
    }


    /**
     * End the current request. It is acceptable to write extra bytes using
     * buffer.doWrite during the execution of this method.
     */
    @Override
    public long end()
        throws IOException {
        if (compressionStream == null) {
            compressionStream = new JGZipOutputStream(fakeOutputStream, new byte[128]);
        }        
        compressionStream.close();
        return ((OutputFilter) buffer).end();
    }


    /**
     * Make the filter ready to process the next request.
     */
    @Override
    public void recycle() {
        // Set compression stream to null
        compressionStream = null;
    }


    // ------------------------------------------- FakeOutputStream Inner Class


    protected class FakeOutputStream
        extends OutputStream {
        protected ByteChunk outputChunk = new ByteChunk();
        protected byte[] singleByteBuffer = new byte[1];
        @Override
        public void write(int b)
            throws IOException {
            // Shouldn't get used for good performance, but is needed for 
            // compatibility with Sun JDK 1.4.0
            singleByteBuffer[0] = (byte) (b & 0xff);
            outputChunk.setBytes(singleByteBuffer, 0, 1);
            buffer.doWrite(outputChunk, null);
        }
        @Override
        public void write(byte[] b, int off, int len)
            throws IOException {
            outputChunk.setBytes(b, off, len);
            buffer.doWrite(outputChunk, null);
        }
        @Override
        public void flush() throws IOException {/*NOOP*/}
        @Override
        public void close() throws IOException {/*NOOP*/}
    }


}
Community
  • 1
  • 1
bestsss
  • 11,796
  • 3
  • 53
  • 63
  • Much appreciated. I gotta do some extensive testing before the final result. Will write back... – Daniil Dec 07 '11 at 14:23
  • 1
    @Daniil, You should be able to compile the code vs Tomcat 7, I took the source from the trunk and made few trivial changes. I am still uncertain why tomcat has not released pure java gzip as it easily beats the native impl. performance wise. Now looking at the code above, I hope I can find some time to publish quite a few race condition fixes in tomcat connectors. – bestsss Dec 07 '11 at 14:34
  • I suppose Tomcat 7 team wants to stick with what is provided with JDK as much as possible to improve ease of usage. I'm all for that, just not happy that something as basic as gzip (these days) causes whole thing to break. – Daniil Dec 07 '11 at 16:37
  • @Daniil, dunno about easy of use, the flushing of gzip is NOT supported via standard GZipOutputStream.flush() (the method does nothing but flushing the underlying stream), they trick via changing compression level back and forth to force Z_PARTIAL_FLUSH but actually the one needed is Z_SYNC_FLUSH, i.e. half-backed quick&dirty hack. – bestsss Dec 07 '11 at 17:47
  • I'm baffled as to why you need to write your own GZIPOutputStream as one is provided by jzlib. I haven't tested yet, but seems its unnecessary. – Daniil Dec 07 '11 at 19:02
  • @Daniil, 2 reasons when i wrote it, the lib didn't have and the 2nd one: the version i've provided is skimmed but the original is optimized for in-mem use, i.e it writes directly over byte[] w/o need to copy the small internal buffer. – bestsss Dec 07 '11 at 19:20
  • so what you're saying using jzlib's GZIPOutputStream would be sufficient. I now have no choice but having to go with your suggestion at this point as we crashed again today on JDK7. – Daniil Dec 07 '11 at 20:13
  • @Daniil, I do not know that for sure, I will have to check the source code 1st. Most likely, yes. Basically, i have not updated jzlib as the one being used is stable and totally happy with. As a rule of the thumb I use all open source projects as source and compile/hack if I see fit or encounter troubles. – bestsss Dec 07 '11 at 20:17
  • but seriously just disable gzip compression if you crash, test the solution i offer or any other you can find and deploy it, if you believe it's stable – bestsss Dec 07 '11 at 20:20
  • Its a good rule, unfortunately it doesn't work in our company. Someone later goes and updates the library making some hacks incompatible or who knows what else. So I'll try it out. – Daniil Dec 07 '11 at 20:21
  • You have no idea how many complains I get the site is slow if I disable the compression. Seems that the clients are rather suffer intermittent outages than having slower site. – Daniil Dec 07 '11 at 20:46
  • i hope you have some service to auto-restart tomcat, alternatively you can setup a squid proxy in front of tomcat to do the compression. – bestsss Dec 07 '11 at 20:51
  • Tomcat is being ran by jsvc, so the actual downtime is quite short. Can't use squid due to SSL plus adding more layers creates more complexity and then you get complaints from the admins. See, if JDK wouldn't have this issue, we all would be so much more happy. – Daniil Dec 07 '11 at 22:26
  • @Daniil, I checked the code of the JDK Deflater, it looks pristine... can't find a bug (my C is of course sloppy, but still). It's quite slow since it calls malloc/free on each pass, should have used stack allocation for at least 4-8KB and handle the common case, but I see no issues there, should be the zlib, itself. – bestsss Dec 08 '11 at 00:13
  • Yes, there is nothing wrong with Deflator class. The crash comes from inside libzip. Always. – Daniil Dec 08 '11 at 02:10
  • I meant the native code of the Deflator, i.e. `Java_java_util_zip_Deflater_deflateBytes` – bestsss Dec 08 '11 at 07:35
  • Ran through today. It was slow, not a lot of traffic, good for a release. The only difference is higher CPU usage, which is expected. Will see how Monday goes by. – Daniil Dec 10 '11 at 03:36