Getting Vcard OutofMemory

Hi all, I have a trouble when getting vcard from asmack android.

Sometimes i’m getting outofmemory problems.

can anyone resolve this?

Thx

[CustomUncaughtExceptionHandler]-2014-01-28 10:07:09,975-803607- [ERROR] Uncaught Exception!!!

java.lang.OutOfMemoryError

at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:94)

at java.lang.AbstractStringBuilder.appendNull(AbstractStringBuilder.java:103)

at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:139)

at java.lang.StringBuffer.append(StringBuffer.java:219)

at org.jivesoftware.smack.util.PacketParserUtils.parseContentDepth(PacketParserUti ls.java:221)

at org.jivesoftware.smack.PacketReader.parsePackets(PacketReader.java:248)

at org.jivesoftware.smack.PacketReader.access$000(PacketReader.java:51)

at org.jivesoftware.smack.PacketReader$1.run(PacketReader.java:83)

Looks like parseContentDepth is causing a StringBuffer to fill up until there is no more memory. This shouldn’t happen. Can you show us the stanza(s) that is causing this?

I just code like

VCard vCard = new VCard();

vCard.load(clientConnection.getConnection(), userJID);

bundle.putString(“nickname”, vCard.getNickName());

bundle.putString(“firstname”, vCard.getFirstName());

bundle.putString(“lastname”, vCard.getLastName());

bundle.putString(“email”, vCard.getEmailHome());

bundle.putByteArray(“avatar”, vCard.getAvatar());

bundle.putString(“field”, vCard.getField(“Title”));

bundle.putString(“jabberid”, vCard.getJabberId());

bundle.putString(“organization”, vCard.getOrganization());

bundle.putString(“phone_home”, vCard.getPhoneHome(“Phone”));

bundle.putString(“phone_work”, vCard.getPhoneWork(“Phone”));

bundle.putString(“country_home”,

vCard.getAddressFieldHome(“Street”));

bundle.putString(“country_work”,

vCard.getAddressFieldWork(“Street”));

But sometimes getting Timeout Exception and getting outofmemory issues.

The stanzad is Vcard stanzas

Flow was referring to the stanza for the incoming packet that is being parsed. It can by used to run a test against the current parser to see why it fails.

Run your app that is crashing with -Dsmack.debugEnabled=true and post the offending stanza.

It also happened to me using the aSmack 0.8.10. I caught somes stanzas without being sure which one is the problem. However I noticed in the dump that there were four threads running:

Smack Packet Reader (22)

Smack Packet Reader (24)

Smack Packet Reader (26)

Smack Packet Reader (28)

Probably all of this was due to some bug in my connection reuse code or in the smack when it set XmppConnection.packetReader as null when an exception occurs in the xmppConnection.initConnection() or yet in the packet reader threads conditions to close.

Nevertheless, I got huge StringsBuffers with “nullnullnullnullnull…” from the dump.

I was caused in the PacketParserUtils.parseContentDepth() due to an exception in the IQ processment causing an infinite loop filling the StringBuffer with **parser.getText() **returning null.

A stanza the breaks the parserContentDepth with OOME is “”.

So, I think the PacketParserUtils.parseContentDepth method is not safe enough. Despite any user code errors, an OOME should never happen.

Look the code related

class PacketReader {

else if (parser.getName().equals(“iq”)) {

IQ iq;

try {

iq = PacketParserUtils.parseIQ(parser, connection);

} catch (Exception e) {

String content = PacketParserUtils.parseContentDepth(parser, parserDepth);

UnparsablePacket message = new UnparsablePacket(content, e);

if (callback != null) {

callback.handleUnparsablePacket(message);

}

continue;

}

connection.processPacket(iq);

}

public class PacketParserUtils {

public static String parseContentDepth(XmlPullParser parser, int depth) throws XmlPullParserException, IOException {

StringBuffer content = new StringBuffer();

while (!(parser.next() == XmlPullParser.END_TAG && parser.getDepth() == depth)) {

content.append(parser.getText());

}

return content.toString();

}

}

PS: Why the Openfire is sending me an empty IQ stanza?

Great summary, thank you, that could be it. Will have a look.

PS: Why the Openfire is sending me an empty IQ stanza?
IQ stanzas of type result usually carry no more data.

A stanza the breaks the parserContentDepth with OOME is "".
Could you elaborate that, since I was not able to reproduce this.

I can’t explain how the code flow could be reach at a point like this (maybe in a race), but the following code reproduce the OOME.

Reader reader = new StringReader("<iq type=“result” from=“you” to=“me”/>");

XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();

parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);

parser.setInput(reader);

int parserDepth = parser.getDepth(); // will be 0

String content = PacketParserUtils.parseContentDepth(parser, parserDepth);

I think the additional check can fix it

public String parseContentDepth(XmlPullParser parser, int depth) throws XmlPullParserException, IOException {

StringBuffer content = new StringBuffer();

while (!(parser.next() == XmlPullParser.END_TAG && parser.getDepth() == depth) && parser.getEventType() != XmlPullParser.END_DOCUMENT) {

content.append(parser.getText());

}

return content.toString();

}

I also think the append should be checked

if (parser.getText() != null) {

content.append(parser.getText());

}

Great! That example may gave me a hint and an idea how to fix the issue. Will test and report back.

After having a closer look:

  1. Yes the code example is causing an OOM. But it’s using parseContentDepth() wrongly, ie the parser must always return to a non END_DOCUMENT tag that has the same depth as the depth given as argument to parseContentDepth(). This is not the case in our code example

  2. The proposed additional check for END_DOCUMENT is also not helpful. XMPP streams have the end document tag only when the connection is terminated, but then parseContentDepth is not used.

  3. We now throw an Exception when getText() in parseContentDepth() returns null. This does of course not fix the problem, but is slightly better then eating up the memory and throwing an OOM.

I still have no pointers what’s triggering this

I believe that what was triggering this in my case is the multiple Smack Packet Reader threads.

So, I agree with the solution. If it happens again, at least the “Smack Packet Reader (XX)” thread will end the code.

PS: If I call xmppConnection.connection() twice, do I have two “Smack Packet Reader (XX)” threads running for some instant?

I believe that what was triggering this in my case is the multiple Smack Packet Reader threads.
No, that can’t be the case.

If it happens again, at least the “Smack Packet Reader (XX)” thread will end the code.
That should have been also the case when the OOM exception was thrown. No it’s just a other type of exception that is thrown.

If I call xmppConnection.connection() twice, do I have two “Smack Packet Reader (XX)” threads running for some instant?
connect() should throw an Exception if the connection is already connected.

If it happens again, at least the “Smack Packet Reader (XX)” thread will end the code.

That should have been also the case when the OOM exception was thrown. No it’s just a other type of exception that is thrown.

Actually the OOME (OutOfMemoryError) is not caught by the “Smack Packet Reader” thread because it extends the class Error, not the class Exception.

Ahh right. Thanks for pointing that out.

The difference between throwing an Exception and everything else is that the Exception will get caught later invoking connectionClosedOnError() while everything that is not an Exception will cause the process to terminate.